mercredi 30 novembre 2011

Devoxx

10 ans … Voilà 10 ans que vers cette période de l’année, plus de 3000 passionnés se réunissent à Anvers pour la plus grosse conférence autour de la plateforme java. Elle se découpe en 2 parties : université et conférence.
Pour moi, c’était ma 3ème participation. Par contre, cette fois ci, j'y suis allé pour la semaine complète avec mes collègues Vincent Bostoen et Benito d’Almeida. Les autres Sfeiriens, eux, nous ont rejoint le mercredi. Au total, 15 Sfeirens étaient présent. Même si nous sommes partis 2 jours avant les autres, nous avions tout de même le même point de rendez-vous avant le départ. Malgré une petite mésaventure dans les transports en commun Anversois, nous sommes arrivés à bon port au cinéma multiplexe où a lieu la conférence.
Cette année, gros changement dans l’organisation : au lieu d’un simple badge avec de petits tickets à échanger contre les repas, nous avons reçu un bracelet marqué de notre nom et d’un code barre. Heureusement, ce bracelet était waterproof car nous devions le garder au bras pendant la semaine entière.
C’est donc le mercredi qu’à la station de tramway nous avons rencontré nos autres collègues pour la 2ème partie de l’aventure : la conférence. Celle ci a commencé par l’habituelle keynote de Stephan Jansen, l’organisateur. Pour finir son discours, il nous a montré une vidéo. On y reconnaît quelques têtes connues : les organisateurs du Paris JUG... puis s’affiche “Devoxx France” … Et oui, au mois d’avril, nous aurons une conférence affiliée à Devoxx, ici à Paris, avec 75% des sessions en français. Sfeir est d’ailleurs sponsor de l’évènement.
Le retour s’est fait le vendredi après midi, après une semaine plutôt fatigante.


Mes impressions globales :

  • On sent vraiment que Google est partenaire : nous avons eu beaucoup de sessions autour d’Android et Html5.
  • Même chose en avec le duo Spring Source/Cloud Foundry
  • Les langages jvm ont du succès : Scala a eu droit à beaucoup de sessions et remplaçait même java pour des exemples de codes. Ceux un peu moins populaires : Fantom et Clojure ; et les petits nouveaux : Kotlin et Ceylon venaient se présenter.
  • Moins de sessions cloud que les années précédentes.
  • Plus d'animations aux stands : course de petites voitures sur circuit électrique, roulette de casino virtuelle, ...
  • Pas de grosses annonces (mis à part Devoxx France)
  • Comme toujours, beaucoup de monde.
  • De plus en plus de français.

Parmi les sessions qui m'ont le plus intéressé :

  • JAX-RS 2 qui disposera d'une (meilleure) intégration avec d'autres apis comme Bean Validation (JSR-303), et surtout d’une partie cliente.
  • MongoBD. Je l'avais récemment découvert pour un POC où je voulais stocker des données en ligne, la session m'a permis de mieux voir les possibilités qu'il offre.
  • Performance et usabilité Android : beaucoup de conseils divers de développement à ne pas oublier si l’on veut une application qui soit ergonomique et performante.
  • Android sur Google TV, même si on est loin d'avoir ces télévisions aussi répandues que des smartphones, il est tout de même intéressant de voir les enjeux et l'ergonomie.

Pour ceux qui n’ont pas pu venir et qui seraient intéressés pour revoir les sessions, ou pour ceux qui souhaitent voir certaines sessions qu’ils n'auraient pas pu voir : il y a Parleys. Ce site offre en effet la possibilité de revoir les sessions de Devoxx, mais aussi de certains jugs locaux. Les sessions Devoxx seront disponibles gratuitement pour ceux qui y ont participé, via abonnement pour les autres. Il faudra quand même patienter jusqu’à la fin de l'année pour qu'elles soient disponibles....



Photo par Pierre-Antoine Grégoire.

lundi 27 juin 2011

Code Retreat #1

Ce 24 juin, j'ai assisté à un code retreat, organisé par Jean-Laurent de Morlhon et Simon Caplette, chez Xebia.
Avant de m'inscrire, je n'en connaissais pas le principe. Je me réfère donc à la définition donnée par les organisateurs :
"Le principe est le suivant: sur la journée coder en binôme sur plusieurs sessions de 30-45 minutes chacune afin de résoudre un problème. A l’issue de chaque session on change de binôme et tout le code produit est effacé. Drastique mais efficace pour rester créatif. C’est aussi le lieu privilégié pour tester de nouvelles techniques et algorithmes."
C'est un concept de coding dojo que je n'avais essayé, d'où mon envie de tester.

Le temps que tout le monde soit là et installé, un traditionnel petit déjeuner nous attendait.
Avant de commencer, l'organisation de la journée nous est présenté :
- Le but n'est pas de finir, mais de faire ce que l'on peut
- Par recommmencement on finit par s'améliorer, on cherche à améliorer la façon de faire.
- Une session est composé de 40 min de développement en pair programming et 20 min de débriefing général.
- Ici c'est différent du boulot, on a le temps, inutile de rusher. Nous sommes ici pour apprendre, tester d'autres façons de faire, essayer un autre langage ou un autre ide, découvrir des plugins d'ide, ...
- Procéder par TDD et suivi les principes de YAGNI. Si ce sont des pratiques que l'on ne fait pas au quotidien dans notre travail, c'est justement l'occasion de s'y exercer
- Une session est composé d'une partie de développement de 40 min en pair programming, suivi de 10-15 min de retrospective globale.
- Une retrospective globale en fin de journée

Souvent le jeu de la vie est utilisé en code retreat, ici pour changer ce sera TicTacToe dont les spécification nous sont fournis en 6 règles :
1/ Une partie est finie lorsque tous les champs sont pris.
2/ Une partie est finie lorsque une colonne est prise par un joueur.
3/ Une partie est finie lorsque une ligne est prise par un joueur.
4/ Une partie est finie lorsque une diagonale est prise par un joueur.
5/ Un joueur peut jouer lorsque un champ n'est pas pris.
6/ Les joueurs jouent à tour de role jusqu'à ce que la partie soit finie.

Au total, sur la journée 5 sessions auront lieu.


Session 1


Au bilan de cette première session, certains remonte l'usage de plugins Eclipse :
- Pair Hero : duels en pair programming où chacun code à tour de role jusqu'à ce qu'un test échoue. Des points sont gagnés en fonction de refacto ou rapidité à faire passer le clavier.
- Infinitest : lance les tests unitaire à chaque enregistrement du code, économise ainsi des switch test/code.
Beaucoup prennent les régles dans l'ordre, mais est ce bien utile ?
Quant à l'implémentation, le plateau est codé sous diverse forme : tableau à 2 dimensions, chaine simple, ...


Session 2


Les noms de classes eux aussi sont variés : TicTacToe, Party, Board, ... tout comme les méthodes de fin : checOver, isOver(), ....
Des questions sur le moment où refactorer se posent. Le mieux est quand on veux, quand celà nous semble un moment logique, une étape. Et il ne faut pas oublier que la refacto concerne aussi bien, le code métier que les tests.
Quelques personnes pense qu'ils ont introduit la notion de joueur trop tôt, ils auraient pu refactorer lorsqu'ils en auraients eu besoine.
Profitant de l'occasion de pouvoir refactorer, certains ont changé leur design, parfois avec regret. Mais y trouve tous l'avantage du recommencement, qu'ils ne peuvent pas faire au boulot.


Session 3


Quelques-uns trouvent un avantage dans l'implémentation de chaine de caractère, pour une raison de facilité de recherche interne.
Pour ma part, après 2 sessions tournées sur la détection de fin de partie, cette fois ci, mon code était plutôt orienté vers la le tour par tour des joueurs.
Une problématique courante remonte : placer le jeu dans un état précis pour tester par exemple des configuration de plateau. Une solution possible serait d'avoir une méthode d'initialisation.
Les animateurs remontent qu'ils entendent parler d'architecture, mais qu'il s'agit là d'une notion bien poussée, car la seule voulue, c'est un simple jeu.

Pause repas


Après toutes ces aventures de la matinée, nous avons le droit à un repas geek : pizza/coca !
Pour l'après midi, pour ceux qui souhaitent, on monte le niveau avec des objectifs :
- No Mouse : souris interdite, uniquement clavier.
- 5 min par personne : on alterne le clavier toutes les 5 minutes.
- Tdd as if you meant it : Tdd encore plus poussé avec des régles telle que coder son code métier dans la classe de test.

Session 4


Pour cette session, 2 groupes ont testé un autre langage :
- JavaScript (Mathilde Lemée et Eric Le Merdy) : Utilisation de JQuery, structure de données portée par la page. QUnit ne semble pas super pour tester du js.
- Ioke (David Gageot et moi même) : Je ne connaissais absolument pas ce langage, j'ai donc était volontaire pour le découvrir. C'est un langage entre lisp, ruby et smalltalk qui tourne aussi bien sur une VM java que .Net. Nos tests sont assez très lisible, au contraire du code. En effet, de refacto en refacto et par jeu, nous avons réduit de façon très importante le code en utilisant toute la puissance du langage. Si bien que notre code pour les régles lignes et colonnes tient en 3 lignes.
Ceux qui avaient l'objectif, Tdd as if you meant it sont satisfaits, par le fait d'avoir pu repousser le moment de décider où placer son code.

Session 5


Le groupe JS a poursuivi ce qu'il avait commencé et nous montre leur réalisation. Le js a permit de connecter facilement à une ihm html, la structure de données est directement dans le dom plutôt que dans des objets js.
David a continué en Ioke avec un autre volontaire pour découvrir le langage. De nouvelles refacto ont eu lieu, et le code pour tester les 4 ères régles tiennent sur environ 6 lignes.
Nos animateurs nous montrent un exemple de Tdd as if you meant qu'ils avaient réalisé. Le retardement du choix du placement du code a fait qu'ils ont eu une approche fonctionnelle, où le modèle est sans état.
Et pour finir, ils énumèrent quelques possibilité de challenge possible en code retreat : No loop, No If, ...
Invonlontairement, ces 2 challenges ont été vaincus par David et son code en Ioke.


Debriefing de la journée


Nous finissons la journée sur une rétrospective finale sur ce que nous avons appris et ce qui sera pratiqué au retour au travail. Dans le désordre :
- Découverte du tdd et voir quelle limite dans l'approche du code nécessaire à faire passer le test.
- Le tdd permet de penser plus au fonctionnel.
- Necessité de plus pousser le principe de code minimal afin d'affiner ses tests.
- Réticence à Infinitest vaincu. Pour des tests volumineux, il est paramétrable.
- Un autre langage qui faire penser différement.
- Pair Hero assez plaisant
- Parfois on pense avoir résolu de manière optimale un problème mais après avoir recommencé avec une autre personne, on se rend compte que non.
- Progression dans les tests car parfois quasiment pas de tests dans les projets au boulot.
- Nouvelle vision à chaque fois.
- Du test fait mais pas tdd, ce qui fait un nouvel outil à utiliser.
- Il peut être utile d'avoir une phase de réflexion avant de se lancer dans les tests.
- Des discussions intéressantes.
- Passer plus de temps sur la refacto.




Un autre Code Retreat aura lieu le 2 juillet, avec Oana Juncu de Sfeir comme animatrice.
A l'heure actuelle, il reste quelques places dépéchez si vous être intéressés.

mercredi 22 juin 2011

Bilan Cloud Camp 1 chez Sfeir

C'est le 30 mai dernier qu'a été organisé le 1er Cloud Camp. Plus de 50 personnes étaient présentes au rendez vous.


A l'arrivée, un accueil convivial avec des cacahuètes et boisson attendaient les invités.
La soirée commence avec une présentation de Kohsuke Kawaguchi, sur le build en parallèle avec Jenkins.



Dans la foulée, Steven Noels lui aussi vient aussi nous parler de son bébé : Lily



C'est autour de pizza que nous nous remettons de nos émotions, ce qui est aussi l'occasion de discuter entre différentes personnes présentes.

C'est après, s'être bien rempli l'estomac que la soirée, s'est poursuivies etlLes sessions camp organisées furent :
  • Forplay : le framework de Google pour réaliser des jeux pour des cibles Gwt/Android/Flex/Swing
  • Retour d'expérience sur Appengine
  • Mvp4g
  • Démo de Lilly
  • CloudBees
Le Bilan que je fais de cloud camp est très positif et a plus au participant. Nous avons vu défiler les tweets lors des présentation de nos guest start. Et quelques sujets de blog de participants d'Exilys sur Lily et ForPlay.

D'autres sessions, seront probablement organisée. Nous espérons vous y voir.

jeudi 16 juin 2011

La marmite des Duchess

La 1ère édition de la Marmite des Duchess a eu le 7 juin dernier. Nom de code "Crumble".
Ce nouvel évènement, proposait 2 sessions en parallèle, le choix se faisait à l'inscription :
- Atelier Mockito, animé par Mathilde Lemée et David Gageot. (limité à 20 places)
- Open Space Technologique. (limité à 40 places)
Doit on y voir l'anologie du crumble dans ce découpage ? Je ne saurais répondre à cette question.

Au total, une trentaine de personnes (dont 6 Sfeirien(ne)s) étaient présentes pour une durée d'un peu plus de 2 heures.

Atelier Mockito





C'est la partie à laquelle j'ai assisté.
Cet atelier était basé sur le code de tudu-list. Le but était de, en binome, compléter des tests et de les faire passer au vert. Pour palier au différente connaissance et niveau des participants, ils étaient réparti en 3 niveau de difficulté.
Pour ma part, connaissant un peu Mockito, j'ai fait la partie niveau 2. Nos animateurs étaient bien sûr présent pour nos différentes questions sur la façon de faire/organiser nos test aussi bien sur Mockito mais aussi sur FestAssert.
Ces exercices permettent d'avoir quelques bases sur Mockito en découvrant sa syntaxe et ses particularité et aussi si nous sommes rigoureux avec le formalisme BDD qui est conseillé dans l'écriture des tests mais aussi de

Si vous souhaitez tenter le défi, le code à compléter est ici et la solution .

Open Space Technologie




N'ayant pas de don d'ubiquité, je n'était pas présent à cette partie. Mais heureusement, mon collègue Clément Griffoin avait pris des notes.

Différents sujets ont étés abordés dont :
- Women in java
- ForPlay où Pierre-Yves Ricau fera une démonstration de sa réalisation
- Déploiement continu
- Stubs vs Mock
- TDD/DDD/BDD


Vous pourrez retrouver plus d'information ce qui s'est dit dans cette soirée sur le blog des Duchess.

Et pour finir, c'est un autour d'un repas que les derniers motivés se sont retrouvés.


Félicitations pour ce premier édition ! Et vivement la suite à la rentrée.

Des photos de la soirée sont disponibles

Du protobuf dans mon Jersey

J'avais déjà parlé, dans de précédents articles, de la génération de xml et de json avec Jersey. Et si maintenant, on s'amusait à générer du protobuf ?

On parle de protobuf pour Protocol Buffers, une techno Google pour encoder des structures de données. Ce format de données compact est utilisé en interne chez Google des échanges de données.
Etant basé sur la déclaration d'une structure de données dans un idl, protobuf possède plusieurs implémentation et est ainsi utilisable dans plusieurs langage.
En java, la génération du code cible se fait avec ant. Mais bien sur reste utilisable avec maven par le plugin ant.

Nous allons reprendre notre Hello qui avait d'exemple. Voici sa structure protobuf :

package nfrancois.poc;

option java_package = "nfrancois.poc.protobuf.model";
option java_outer_classname = "HelloProto";

message Hello {
  required string name = 1;
  required string message = 2;
}

La structure se comprend assez facilement. Attention par contre, au trompeur package de 1ère ligne, qui n'est pas lié à la notion de package que nous avons en java. Il sert comme espace de nommage et éviter des collisions de nom si plusieurs objets protobuf portent le même nom. Puisque depuis cette idl, je pourrai aussi bien générer en C++ ou un autre langage, le vrai nom de package java est indiqué par l'option "java_package", de la même façon pour le nom de classe qui va tout encapsuler qui sera "java_outer_classname"

Pour plus d'information sur protobuf, je vous invite à consulter sa page google code.

Le générateur protobuf générera un fichier HelloProto.java, qui permettra de manipuler les Hello : création via pattern builder, encodage/désencodage, ...
Le "vrai" Hello sera encapuslé au sein de ce dernier.
Comme je disais, je génère le java par le ant plugin de maven :


 org.apache.maven.plugins
 maven-antrun-plugin
 1.6
 
  
   generate-sources
   generate-sources
   
    
     
             
             
             
    
   
   
    run
   
       
 

et bien sûr des dépendances protobuf


    com.google.protobuf
    protobuf-java
    2.4.0a


Le contrat de test sera assez proche de se que nous avions dans les tests précédents :

@Test
public void shoulReplyHello(){
 // Given
 String message = "Hello";
 String name ="Nicolas";
 Hello hello = HelloProto.Hello.newBuilder().setName(name).setMessage(message).build();
 when(helloServiceMock.saysHelloToSomeone("Nicolas")).thenReturn(hello);
 // When
 ClientResponse response = resource().path("hello").path(name).get(ClientResponse.class);
 // Then
 verify(helloServiceMock).saysHelloToSomeone(name);
 assertThat(response.getClientResponseStatus()).isEqualTo(Status.OK);
 assertThat(response.getType().toString()).isEqualTo("application/x-protobuf");
 Hello entity = response.getEntity(Hello.class);
 assertThat(entity).isNotNull().isEqualTo(hello);
}

La resource REST, elle aussi va peut évoluer :

@Path("hello")
@Singleton
@Produces("application/x-protobuf")
public class HelloResource {
 
 @Inject
 private HelloService helloService;
 
 @GET
 @Path("/{name}")
 public Hello reply(@PathParam("name") String name){
  return helloService.saysHelloToSomeone(name);
 }
 
 public void setHelloService(HelloService helloService) {
  this.helloService = helloService;
 }
 
}


La difficulté à laquelle il faut se confronter, c'est que Jersey ne permet pas de gérer de base le protobuf… Pas grave, on va s'occuper de faire le lien entre l'encodage/désencodage de protobuf et Jersey.


Commençons par le reader qui s'occupe de désencoder le protobuff. Pour celà, nous devons implémenter l'interface MessageBodyReader où nous aurons du code technique protobuf.

@Provider
@Consumes("application/x-protobuf")
@Singleton
public class ProtobufMessageBodyReader implements MessageBodyReader<Message> {

 public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
  return Message.class.isAssignableFrom(type);
 }

 public Message readFrom(Class<Message> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
  try {
   Method newBuilder = type.getMethod("newBuilder");
   GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(type);
   return builder.mergeFrom(entityStream).build();
  } catch (Exception e) {
   throw new WebApplicationException(e);
  }
 }

}


C'est par le content type "application/x-protobuf" que JAX-RS fera matcher le type le reader/writer à l'entrée/sortie de la resource.
Pour l'encodage, c'est l'interface MessageBodyWriter qu'il faut implémenter.

@Provider
@Produces("application/x-protobuf")
@Singleton
public class ProtobufMessageBodyWriter implements MessageBodyWriter<Message> {
 public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
  return Message.class.isAssignableFrom(type);
 }

 private Map<Object, byte[]> buffer = new WeakHashMap<Object, byte[]>();

 public long getSize(Message m, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  try {
   m.writeTo(baos);
  } catch (IOException e) {
   return -1;
  }
  byte[] bytes = baos.toByteArray();
  buffer.put(m, bytes);
  return bytes.length;
 }

 public void writeTo(Message m, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
  entityStream.write(buffer.remove(m));
 }
}

La configuration de test, quant à elle sera un peu plus complexe, car il faut que la partie cliente puisse désencoder toute seule le protobuf :

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

mercredi 25 mai 2011

Le Scroll horizontal facile en Android

Dans un souci d'ergonomie, le scroll horizontal est bien appréciable, par exemple pour passer à l'écran suivant.
Même si la fonctionnalité est utilisé, pour se déplacer entre les écrans du home, il n'existe pas de composants qui gère cela. Comme bien, souvent, c'est dans l'open source que l'on trouve une solution.
C'est sur github que j'ai trouvé View Flow for Android qui offre une solution simple à intégrer.
En fin de compte, ce comportement de scroll horizontal, c'est une sorte de ListView à défilement horizontal où une cellule = un écran. C'est sur ce principe que se base cette api.
Elle m'offre un composant ViewFlow qui aura un nécessitera un BaseAdapter pour réaliser l'affichage de chacun de ses écrans.

viewFlow = (ViewFlow) findViewById(R.id.viewflow);
viewFlow.setAdapter(new MonAdapter());



Qu'est ce que ce sidebuffer ?
Il s'agit tout simplement du buffer des écrans chargés; ce qui permet de fluidifier le scroll. Avec la valeur 3, j'aurai jusqu'à 3 écrans à droite, 3 écrans à gauche ainsi que mon écran actuel, soit 7 écrans, qui sont chargés et en mémoire. Cette valeur, est à 3 par défaut

Et si j'ai besoin d'écouter le changement d'écran ?
Ca tombe bien, il existe un ViewSwitchListener

viewFlow.setOnViewSwitchListener(new ViewSwitchListener() {
    public void onSwitched(View v, int position) {
        // Your code here
    }
});

Certains Home Android, affiche un indicateur de position afin de connaitre celui sur lequel nous nous trouvons. View Flow for Android offre aussi cette possibilité.
A l'heure actuelle, il en existe 2
  • Cercle
    CircleFlowIndicator indic = (CircleFlowIndicator) findViewById(R.id.viewflowindic);
    viewFlow.setFlowIndicator(indic);
    



  • Texte
    TitleFlowIndicator indicator = (TitleFlowIndicator) findViewById(R.id.viewflowindic);
    indicator.setTitleProvider(myTitleProvider);
    viewFlow.setFlowIndicator(indicator);
    


Je trouve cette api sympathique à utiliser, et espère y voir de nouvelles fonctionnalités.
Comme beaucoup pour beaucoup de projet sur github, les contributions sont les bienvenues, si vous avez des idées, n'hésitez pas.

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.

mercredi 30 mars 2011

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

Jersey et Guice offre son un assemblage intéressant pour exposer un service REST sur App Engine. Mais comment puis je tester convenablement de cocktail ?
Dans son api, Jersey propose la classe JerseyTest qui permet de démarrer un server en mémoire ainsi que quelques méthodes d'aide pour construire ses requêtes.
En réalité, le fait d'être sur AppEngine, ne va rien changer à ce que nous en ferons au sein de cet article.

Un peu de code

Nous allons utiliser une resource toute simple : Hello.
Pour cet article, nous allons commencer par utiliser un type de retour simple lui aussi : du texte brut. Nous disposerons de 2 méthodes, un GET et un POST.

Voici notre ressource :

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

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

Je ne vais pas rentrer dans les détails de l’implémentation, ce n'est pas le but de cet article.
Le HelloService utilisé est le suivant :

@Singleton
public class HelloService {
 
 public String saysHelloToSomeone(String name){
  return "Hello "+name;
 }

 public String sendHello(String name) {
  return "Hello "+name;
 }

}

Et pour finir, nous aurons la configuration Guice suivante pour fonctionner correctement avec Jersey :

public class GuiceServletConfig extends GuiceServletContextListener {
 
 private static final String SDAAS_SERVER_RESOURCES_PACKAGE = HelloResource.class.getPackage().getName();
 private static final String JERSEY_CONFIG_PROPERTY_PACKAGES = "com.sun.jersey.config.property.packages"; 

 @Override
 protected Injector getInjector() {
  final Map params = new HashMap<String, String>();
  params.put(JERSEY_CONFIG_PROPERTY_PACKAGES, SDAAS_SERVER_RESOURCES_PACKAGE);
  
  return Guice.createInjector(new ServletModule() {

   @Override
   protected void configureServlets() {
    serve("/*").with(GuiceContainer.class, params);
   }
  });
 }
}

Les ressources exposées sont découvertes automatiquement par l'utilisation du paramètre de clé JERSEY_CONFIG_PROPERTY_PACKAGES. Il leur suffit donc que mes ressources soit dans le package pointé par la valeur de ce paramètre pour qu'elle soit prise en compte.

Et un zeste de test pour garder la fraicheur

Il nous reste plus qu'à mettre en place quelques tests dessus.
Il v falloir créer une classe de test qui hérite de JerseyTest.
Comme je disais, celui ci va lancer son serveur en mémoire, il nécessite donc une dépendance supplémentaire :

<dependency>
   <groupid>com.sun.jersey.jersey-test-framework</groupid>
   <artifactid>jersey-test-framework-grizzly</artifactid>
   <version>${jersey.version}</version>
   <scope>test</scope>
</dependency> 

Fonctionnant par un mécanisme spi, il n'aura pas besoin de configuration supplémentaire pour se lancer.
JerseyTest n'utilise pas le web.xml et doit donc être configuré par du code; soit par le constructeur, soit par l'implémentation de la méthode protected AppDescriptor configure(). Nous allons privilégier le second choix. D'autant plus que nous allons devoir utiliser un autre Injector Guice pour pouvoir mocker HelloResource, sans quoi nous ne pourrons pas récupérer l'instance de HelloResource. Un autre avantage est d'isoler la ressource que je souhaite tester.

private static Injector injector; 
private HelloService helloServiceMock;

@Before
public void setUp() throws Exception {
 super.setUp();
 HelloResource helloResource =  injector.getInstance(HelloResource.class);
 helloServiceMock = mock(HelloService.class);
 helloResource.setHelloService(helloServiceMock);
} 

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

private static class GuiceTestConfig extends GuiceServletContextListener {
 @Override
 public Injector getInjector() {
  return injector;
 }
}


Il n'y a plus qu'à se créer des méthodes de tests

@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");
 
}

@Test
public void shouldSendHello(){
 String name ="Nicolas";
 String hello = "Hello "+name;
 when(helloServiceMock.sendHello(name)).thenReturn(hello);

 ClientResponse response = resource().path("hello").post(ClientResponse.class,name);

 verify(helloServiceMock).sendHello(name);
 assertThat(response.getClientResponseStatus()).isEqualTo(Status.CREATED);
 assertThat(response.getType()).isEqualTo(MediaType.TEXT_PLAIN_TYPE);
 assertThat(response.getEntity(String.class)).isEqualTo("Hello Nicolas");
}

Je vérifie que lorsque j'appelle la bonne url, mon résultat correspond à ce que je veux, soit un Hello qui m'est adressé, au bon format et le bon code retour.
Dans le prochain épisode, nous ajouterons une sérialisation JAX-B.



Le code est disponible ici.

Références :
http://blog.iparissa.com/google-app-engine-jax-rs-jersey/

lundi 14 mars 2011

Etendre les assertions de Fest Assert

C'est lors de la présentation de David Gageot sur les test au Paris JUG du mois de janvier que j'ai découvert Fest-Assert. J'ai rapidement été enthousiaste sur son utilisation. En plus de sa syntaxe proche d'un langage naturel, il permet d'étendre ses assertions en fonction de ses besoins.
Voici 2 façons d'utiliser ce mécanisme :

Utilisation d'une Condition.

Ce cas de figure est à utiliser lorsque l'objet que je souhaite vérifier possède déjà son objet d'assertion (StringAssert, FileAsset, ... ) ou si celle ci est simple.
Il faut pour cela étendre la classe Condition<T> et implémenter  la méthode public boolean matches(<T> value).
Imaginons pas exemple que je souhaite vérifier que ma liste contienne un nombre impair d'éléments. Je vais écrire la conditions suivantes :

private class IsListeTailleImpaireCondition extends Condition<List<?>> { 
 
     @Override 
     public boolean matches(List value) { 
          if(value == null){ 
               return false; 
          } else { 
               return value.size()%2==1; 
          } 
     } 
 
} 
Son utilisation s'avère très simplement :
assertThat(maListe).is(new IsListeTailleImpaireCondition()); 
ou :
assertThat(maListe).satifies(new IsListeTailleImpaireCondition()); 

La différente entre les 2, n'est qu'au niveau sémantique, la validation est équivalente.
Et si on veut vérifier le contraire alors ? Rassurez vous, pas besoin d'écrire une condition inverse, Fest-Assert possède des méthodes pour ça : .isNot(...) ou .doesNotSatisfy(...)


Créer son objet d'assertion.

Ce cas de figure est adapté aux cas où mon objet ne possède pas son objet d'assertions. Là, depuis la version 1.4 qui date d'il y a peu, c'est devenu beaucoup plus simple. : il suffit d'étendre la classe GenericAssert et d'implémenter les méthodes que je souhaite, sans oublier bien sûr de retourner une instance de l'objet d'assertion afin de pouvoir chaîner les méthodes.
Imaginons que je souhaite développer mes assertions sur DateTime de jodatime :
import static org.fest.assertions.Formatting.inBrackets; 
import static org.fest.util.Strings.concat; 
 
import org.fest.assertions.GenericAssert; 
import org.joda.time.DateTime; 
 
 
/**
 * Assertions for <code>{@link org.joda.time.DateTime}</code>.
 */ 
public class DateTimeAssert extends GenericAssert<DateTimeAssert, DateTime>  { 
       /**
         * Creates a new {@link org.joda.time.DateTimeAssert}.
         * @param actual the target to verify.
         */  
 public DateTimeAssert(DateTime actual) { 
     super(DateTimeAssert.class, actual); 
 } 
 
 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est avant celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */ 
 public DateTimeAssert isBefore(DateTime expected){ 
     isNotNull();   
     if(actual.isBefore(expected)) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be before to :", inBrackets(expected))); 
 } 
  
 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est avant ou égale à celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */ 
 public DateTimeAssert isBeforeOrEquals(DateTime expected){ 
     isNotNull();   
     if(actual.compareTo(expected) <= 0) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be before to :", inBrackets(expected))); 
 }  
  
 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est après celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */  
 public DateTimeAssert isAfter(DateTime expected){ 
     isNotNull();   
     if(actual.isAfter(expected)) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be after to :", inBrackets(expected))); 
 } 
  
 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est après ou égale à celui passé en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} avec lequel on compare.
  * @return L'objet d'assertion.
  */  
 public DateTimeAssert isAfterOrEquals(DateTime expected){ 
     isNotNull();   
     if(actual.compareTo(expected) >= 0) { 
      return this; 
     } 
     failIfCustomMessageIsSet(); 
     throw failure(concat(actual(), " should be after to :", inBrackets(expected))); 
 }  
  
 /**
  * Vérifie que le {@code org.joda.time.DateTimeAssert} est compris dans l'intervale de ceux passés en paramètre.
  * @param Le {@code org.joda.time.DateTimeAssert} de début d'intervallle
  * @param Le {@code org.joda.time.DateTimeAssert} de fin d'intervallle
  * @return L'objet d'assertion.
  */ 
 public DateTimeAssert isBetween(DateTime begin, DateTime end){ 
     isNotNull(); 
     if(actual.compareTo(begin) >= 0 && actual.compareTo(end) <= 0) { 
         return this; 
     } 
     throw failure(concat(actual(), " should be between :", inBrackets(begin), " and ", inBrackets(end)));   
 } 
  
 
 private String actual() { 
     return inBrackets(actual); 
 } 
  
} 
Pour m'en servir, je dois le rattacher à un point d'entrée, comme Assertions classe de base de Fest-Assert.
public class MyAssertions { 
 
 public static DateTimeAssert assertThat(DateTime actual) { 
     return new DateTimeAssert(actual); 
 } 
  
} 
Et pour finir, je n'aurais plus qu'à faire par exemple :
MyAssertions.assertThat(maDate).isBeetwen(debutPeriode,finPeriode);