insideIT.fr : le blog des architectes IT de SFEIR

Aller au contenu | Aller au menu | Aller à la recherche

lundi 3 janvier 2011

Retour d'expérience d'un projet GWT : 3 Intégration facile de maquette Html en GWT 2 (3/8)

Le contexte:

Le projet a pour but d'améliorer l'ergonomie et l'usabilité du back office d’un produit en cours de développement réalisé en collaboration avec SFEIR

Dans le cadre de ce projet, la version de GWT utilisée passe de la 1.7.1 à la 2.0.3 afin de bénéficier des nouvelles fonctionnalités offertes par cette version. De nouveaux pattern de développement ont également été mis en place au cours de ce projet.

Voici une série d'article sur les nouveautés de GWT 2, les choix d'architectures, et bibliothèques utilisés qui font part de notre retour d'expérience sur le sujet. Ces articles ont été écris par David Aboulkheir, Patrice de Saint Steban et Cyril Lakech

  1. Nouveautés de GWT 2.0
  2. UiBinder, enfin une forte collaboration entre le designer et le développeur
  3. Intégration facile de maquette Html en GWT 2
  4. Architecture Modèle-Vue-Presenteur
  5. Implémentation Modèle-Vue-Présenteur
  6. Ecrire des tests unitaires avec Mockito
  7. Mise en place de Gin sur le projet
  8. Internationalisation

Exemple d'intégration d'un composant d'une page Web dans un projet GWT 2:

Nous devons intégrer un nouveau composant qui nous est livré sous la forme de maquette HTML qui se compose de fichiers HTML, d'image et éventuellement de fichier CSS.

Voici le fichier HTML fourni, il contient le code HTML et le style CSS utilisé:

<style>
<!--

* {
    font-family:calibri;
    font-size:12px;
}

.menu {
    padding: 0px;
    background: url('bgmenu.gif') repeat-y right top white;
    display:block;
    color:#1783c4;
    width:250px;
    height:100%;
}

.menu h2{
    font-family:calibri;
    font-size:18px;
    background: url('menutop.gif') no-repeat bottom left;
    width:250px;
    padding-bottom:10px;
    margin:0px;
}

.menu .close{
    float:right;
    margin:4px;
    margin-right:7px;
}

.menu textarea {
    width:240px;
    min-height:300px;
    border:1px solid #dddddd;
    background: #f6f6f6;
    margin-top:20px;
    margin-bottom:5px;
}

.pages{
    padding:3px 7px 7px 7px;
}

.pages.selected{
     background: url('menubottom.gif') no-repeat bottom left #f5f5f5;
}

.pages .close{
    float:right;
}

.pages a.element{
    color:black;
    font-size:13px;
    font-weight:bold;
    margin-bottom:3px;
    text-decoration:none;
    display:block;
}

.pages a.element:hover{
    text-decoration:underline;
    background:url('croixnoire.gif') 223px 3px no-repeat;
}

.pages .close{
    float:right;
    width:13px;
    height:11px;
}

.right {float:right;}

a.bouton {
    color:white;
    font-size:12px;
    font-weight:bold;
    text-transform:uppercase;
    margin-right:10px;
    padding:2px 5px;
    text-decoration:none;
    font-family:calibri;
    border:1px solid #909090;
    text-align:center;
}

a.bouton.bleu {
    background:#0099cb;
}

a.bouton.bleu:hover {
    background:#0fb3e9;
}

a.bouton.vert {
    background:#4ba600;
}

a.bouton.vert:hover {
    background:#5cc803;
}
-->
</style>
<div class="menu">
      <div class="close"><a href="#"><img src="croix.gif" border="0"></a></div>
     <h2>Jean-Pierre Dupont</h2>   
     <div class="pages selected">
       <a href="#" class="element"><img src="1ptrans.gif" border="0" class="close">Ma page personnel (0)</a>
      </div>
     <div class="pages">
        <a href="#" class="element"><img src="1ptrans.gif" border="0" class="close">Autre page (0)</a>
     </div>
    <br/>
     <a href="#" class="bouton bleu">Créer une nouvelle page</a>
     <textarea>
     Ici je peux mettre des notes
     </textarea>
     <a href="#" class="bouton vert right">Enregistrement</a>
</div>

Nous créons un nouvel élément UiBinder que nous appelons HomePageMenu :

UIBinder create

On modifie le fichier HomePageMenu.gwt.xml comme suit:

  • On ajoute les entêtes nécessaires pour UiBinder
  • On modifie la balise <style /> en <ui:style />
  • On remplace la div principale par une balise <g:HTMLPanel />
  • Les libellés que l’on souhaite rendre dynamiques sont remplacés par des balises <g:InlineLabel />, les liens <a /> par des <g:Anchor /> et les <textarea /> par des <g:TextArea />
  • Les nom des styles utilisés sont modifiés, en lieu et place de “monStyle” on utilise "{style.monStyle}"
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
     xmlns:g="urn:import:com.google.gwt.user.client.ui">
     <ui:style>
        * {
            font-family: calibri;
               font-size: 12px;
        }
       
        .menu {
               padding: 0px;
            display: block;
             color: #1783c4;
            width: 250px;
            height: 100%;
        }
       
        .menu h2 {
            font-family: calibri;
            font-size: 18px;
            background: url('module/images/homepage/menutop.gif')
                 no-repeat bottom left;
            width: 250px;
             padding-bottom: 10px;
            margin: 0px;
        }
      
        .menu .close {
            float: right;
             margin: 4px;
            margin-right: 7px;
        }
        
        .menu textarea {
            width: 240px;
                min-height: 300px;
            border: 1px solid #dddddd;
               background: #f6f6f6;
            margin-top: 20px;
             margin-bottom: 5px;
        }
       
        .pages {
               padding: 3px 7px 7px 7px;
        }
       
         .pages.selected {
            background: url('module/images/homepage/menubottom.gif')
                 no-repeat bottom left #f5f5f5;
        }
       
         .pages .close {
            float: right;
        }
       
        .pages a.element {
            color: black;
            font-size: 13px;
            font-weight: bold;
            margin-bottom: 3px;
            text-decoration: none;
             display: block;
        }
       
        .pages a.element:hover {
            text-decoration: underline;
             background: url('module/images/homepage/croixnoire.gif') 223px
                3px no-repeat;
        }
       
         .pages .close {
            float: right;
            width: 13px;
            height: 11px;
        }
       
         .right {
            float: right;
        }
       
         a.bouton {
            color: white;
            font-size: 12px;
            font-weight: bold;
            text-transform: uppercase;
            margin-right: 10px;
            padding: 2px 5px;
            text-decoration: none;
            font-family: calibri;
            border: 1px solid #909090;
             text-align: center;
        }
       
         a.bouton.bleu {
            background: #0099cb;
        }
        
        a.bouton.bleu:hover {
            background: #0fb3e9;
        }
       
        a.bouton.vert {
             background: #4ba600;
        }
       
         a.bouton.vert:hover {
            background: #5cc803;
        }
    </ui:style>
    <g:HTMLPanel styleName="{style.menu}">
       <div class="{style.close}">
            <a href="#">
               <img src="module/images/homepage/croix.gif" border="0" />
            </a>
        </div>
       <h2><g:InlineLabel  ui:field="userLabel">Jean-Pierre Dupont</g:InlineLabel></h2>
        <div class="{style.pages} {style.selected}">
            <a href="#" class="{style.element}">
                <img src="module/images/homepage/1ptrans.gif" border="0"
                    class="{style.close}" />
                Ma page personnel(0)
            </a>
        </div>
         <div class="{style.pages}">
            <a href="#" class="{style.element}">
                <img src="module/images/homepage/1ptrans.gif" border="0"
                    class="{style.close}" />
                Autre page (0)
            </a>
        </div>
        <br />
        <g:Anchor ui:field="newButton" href="#" styleName="{style.bouton} {style.bleu}">
            Créer une nouvelle page
        </g:Anchor>
         <g:TextArea ui:field="noteTextArea">
            Ici je peux mettre des notes
         </g:TextArea>

        <g:Anchor ui:field="saveNoteButton" href="#" styleName="{style.bouton} {style.vert} {style.right}">
            Enregistrer
        </g:Anchor>
     </g:HTMLPanel>
</ui:UiBinder>


Puis on modifie la classe associée de la manière suivante:
/**
*
*/
package module.client.homepage.menu;

import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;

/**
*
*
*/
public class HomePageMenu extends Composite {

    private static HomePageMenuUiBinder uiBinder = GWT
             .create(HomePageMenuUiBinder.class);

    interface HomePageMenuUiBinder extends UiBinder<Widget, HomePageMenu> {
    }

    //Composant GWT que l'on veux utiliser lié avec l'attribut ui:field=""
    @UiField InlineLabel userLabel;
    @UiField Anchor newButton;
    @UiField TextArea noteTextArea;
    @UiField Anchor saveNoteButton;

    public HomePageMenu() {
         initWidget(uiBinder.createAndBindUi(this));
    }

    @UiHandler("newButton")
    public void onNewClick(ClickEvent event){
        //Création d'une nouvelle page
    }
   
    @UiHandler("saveNoteButton")
    public void onNewClick(ClickEvent event){
        saveNote(noteTextArea.getText());
    }

    public void setUserName(String userName){
        userLabel.setText(userName);
    }

}

Comme on a pu le voir, il est très simple d’intégrer des maquettes HTML/CSS en GWT abec UiBinder, ce qui n’était pas le cas avant la version 2 de GWT. Cela permet d’améliorer la collaboration entre le Web Designer et le développeur puisqu’ils utilisent un langage commun le XML/HTML pour réaliser les écrans. Cela permet également au Web Designer, après une rapide prise en main des outils type SVN/Eclipse, de pouvoir intervenir sur le design de l’application directement sans aucune aide du développeur.

Dans notre cas, on a eu l’agréable surprise de voir notre Web Designer créer des écrans GWT lui même, les modifier et même coder en java pour interagir avec l’IHM; modifier un style programmatiquement ou déclencher une action simple.

mercredi 24 novembre 2010

"Innovation Web", la conférence qui monte (16 décembre)





L'association GET
 (G-EFREI Technologies) organise sa conférence pour la troisième année consécutive. 
Celle-ci se tiendra le 16 décembre prochain à l'EFREI.




Pour avoir une description détaillée de l'évenement vous pouvez vous rendre directement sur le site de l'association, dans la section conférence.

En cette journée du 16 décembre, vont se regrouper professionnels et étudiants qui partagent tous la même passion pour les technologies web et les outils de Google. 

Les portes sont ouvertes à tous, gratuitement, dans la limite des places disponibles (≈ 300) et sous condition d'être inscrit sur le formulaire : conf.g-e-t.fr.

Les thèmes & intervenants

  • Android et son avenir par Cyril MOTTIER, Créateur de GreenDroid

  • GWT 2.1 et mobilité par Salvador DIAZ, Développeur GWT chez SFEIR

  • Cloud computing et la monétisation par Didier GIRARD, Directeur Technique de SFEIR

Après la conférence, venez discuter et échanger autour d’un verre lors du cocktail organisé par GET.

Ateliers Android

Deux ateliers Android sont prévus après la conférence aux alentours de 18h, avec notamment Julien Del Rio de Frandroid, Stéphane Guérin, créateur de Appoke et toujours Cyril Mottier. 
Un atelier sera organisé pour les débutants et un autre pour les développeurs confirmés.

Pour plus d’infos : www.g-e-t.fr

Infos pratiques

  • Lieu : EFREI, 30-32 avenue de la république 94800 Villejuif

  • Métro : Villejuif Louis Aragon Ligne 7 (Terminus)

  • Début de la conférence : 13h30

  • Début des ateliers : 18h

  • Inscription gratuite et obligatoire : conf.g-e-t.fr

  • http://www.g-e-t.fr

jeudi 11 novembre 2010

Google Refine - Les dessous de l'outil pour manipuler des données

Google avait fait l'acquisition en juillet dernier de Metaweb, la société derrière le projet Freebase. Pour ceux qui ne connaissent pas Freebase, c'est un peu le wikipedia de la base de données : Des données tabulaires libres sur des sujets variés (Artistes, Films, Livres, etc.) enrichis et maintenus par la communauté. Metaweb avait également un outil opensource nommé "Freebase Gridworks" permettant de manipuler, corriger, transformer des données. Aujourd'hui Google annonce que Gridworks et renommé en Google Refine et passe en version 2.

3 vidéos de présentations sont proposés pour mieux comprendre comment fonctionne Refine. Elles sont visible ici. On y découvre les fonctionnalités principales de Refine qui sont : corriger les données (fusionner certains types, corriger les valeurs abérantes), transformer (par exemple : découper des chaines de caractères pour créer des nouvelles colonnes), enrichir (en appellant des webservices comme googlemap pour geolocaliser, freebase, ...)

Les sources du projet sont sur google code.

Le produit peut être exécuté sur sa machine. Un simple exécutable qui, une fois lancé, démarre un serveur web. Refine s'utilise donc ensuite depuis un simple navigateur web.

Un coup d'oeil rapide au code nous permet de voir quelques unes des technologies utilisées :

  • LessCss et Lessen : Un préprocesseur CSS très à la mode qui lui rajoute quelques élements très utiles comme les variables, l'imbrication, etc.
  • Jetty 6, qu'on ne présente plus
  • Smilie Butterfly Un framework web écrit en Java. Il a été créé par Stefano Mazzocchi, créateur de Apache Cocoon. Ce monsieur a aussi contribué à quelques JSR (JSR 53 - Servlet, 63 - JAXP, et 170 - Java Content Repository). Il travaillait chez Metaweb au moment du rachat et est donc maintenant chez google (oui encore un :-D ) et travaille sur Refine. Les particularités du framework sont : pensé pour créer des applis web modulaires ; bien qu'écrit en java, fait usage de javascript à la fois sur le client et sur le serveur via Rhino.

Sur le navigateur :

  • jquery avec les plugins jquery ui et event stack

Coté build et outils :

  • Ant
  • Des fichiers .projet, .gitignore qui trainent qui indique l'utilisation d'Eclipse et de Git
  • PMD
  • Launch4j en tant que task Ant pour la génération de l'exécutable

Je trouve Butterfly intéressant, on parle beaucoup du javascript coté serveur avec node.js depuis quelques temps. La frontière entre le client et le serveur est toujours difficile à franchir surtout lorsqu'on vient du monde java, malgré les progrès effectués ces dernières années (GWT, Wicket, ...) Node.js sera justement le sujet de mon prochain billet ! J'investiguerai peut être un peu plus Butterfly pour vous en reparler. A suivre donc...

Alexandre. Twitter : @alexandre_t

lundi 8 novembre 2010

Retour d'expérience d'un projet GWT : 2 UiBinder, enfin une forte collaboration entre le designer et le développeur (2/8)

Le contexte:

Le projet a pour but d'améliorer l'ergonomie et l'usabilité du back office d’un produit en cours de développement réalisé en collaboration avec SFEIR

Dans le cadre de ce projet, la version de GWT utilisée passe de la 1.7.1 à la 2.0.3 afin de bénéficier des nouvelles fonctionnalités offertes par cette version. De nouveaux pattern de développement ont également été mis en place au cours de ce projet.

Voici une série d'article sur les nouveautés de GWT 2, les choix d'architectures, et bibliothèques utilisés qui font part de notre retour d'expérience sur le sujet. Ces articles ont été écris par David Aboulkheir, Patrice de Saint Steban et Cyril Lakech

  1. Nouveautés de GWT 2.0
  2. UiBinder, enfin une forte collaboration entre le designer et le développeur
  3. Intégration facile de maquette Html en GWT 2
  4. Architecture Modèle-Vue-Presenteur
  5. Implémentation Modèle-Vue-Présenteur
  6. Ecrire des tests unitaires avec Mockito
  7. Mise en place de Gin sur le projet
  8. Internationalisation

UiBinder, enfin une forte collaboration entre le designer et le développeur

Dans la version 2 de GWT, Google propose aux développeurs de choisir comment coder les écrans, soit en Java uniquement comme c'était le cas jusqu'à présent soit en utilisant une nouvelle fonctionnalité UiBinder, qui permet de coder les écrans en XML, très proche du HTML.

UiBinder permet d'écrire les interfaces graphiques avec un formalisme XML un peu comme on peut le faire avec JSF, Flex, Silverlight ou encore en HTML. Jusque-là, la création d'interface en GWT se faisait en manipulant du code java à la façon native de Swing ou SWT/JFace. On va maintenant pouvoir les écrire en XML et les lier (les binder) avec une classe java qui contiendra le code de notre vue. Le code en sera d'autant plus lisible, maintenable et plus rapide à développer. Cette évolution va permettre également la multiplication d'outils puissants, comme les éditeurs d'interfaces GWT, plus simples à créer, comme GWTDesigner d'Instantiations, qui a été acheté par Google et qui est maintenant disponible gratuitement: http://code.google.com/intl/fr/webtoolkit/tools/download-gwtdesigner.html

Cette nouvelle organisation, sépare la partie présentation (l'écran) de la partie contrôle (le code java). On écrit un fichier XML contenant nos widgets graphiques, puis on les associe avec les objets dans notre classe java où l'on ajoute aussi les évènements de l'IHM. Il est également possible d'écrire notre interface en HTML en y incorporant nos widgets HTML. Le CSS peut se trouver directement dans le fichier d'interface ou dans un ClientBunddle (système GWT2 de gestion des ressources type CSS en java qui sera détaillé ci-dessous).

Prenons une classe Miniature.java, qui permet d'afficher une image et son titre, pour utiliser UIBinder. Commençons par créer le fichier xml :

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:gwt='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
   .mini {
    float : left;
    margin: 20px;
    height: 150px;
    width: 150px;
   }
</ui:style>
   <gwt:VerticalPanel styleName="{style.mini}">
        <gwt:Image ui:field="miniature" />
       <gwt:Label ui:field="label"/>
   </gwt:VerticalPanel>
</ui:UiBinder>

Nous voyons ici, que la racine du XML, ui:UiBinder permet de configurer les namespaces pour utiliser les composants, le namespace "ui" contient les configurations du UiBinder, tandis que le namespace "gwt" permet d'utiliser les composants de base de GWT. La balise <ui:Style> permet d'insérer le CSS utilisé par notre composant. Dans la suite du fichier, on peut accéder à la classe CSS en utilisant la syntaxe {style.nomClasse}. Nous voyons ensuite que le composant GWT racine est le VerticalPanel qui contient une Image et un Label. Pour les composants Image et Label, on a ajouté un attribut ui:field associé à un nom de paramètre, ce qui nous permettra d'accéder au composant depuis notre classe Java.

Voyons maintenant la classe Java associée :

public class Miniature extends Composite {
    interface Binder extends UiBinder<Widget, Miniature> { }

    private static final Binder binder = GWT.create(Binder.class);
    @UiField Image miniature;
    @UiField Label label;
    private String destination;

    public Miniature(String url, String titre, String destination) {
        this.destination = destination;
        initWidget(binder.createAndBindUi(this));
        miniature.setUrl(url);
        label.setText(titre);
    }

    @UiHandler("miniature")
    public void onClick(ClickEvent event) {
        Window.open(destination, "Image", "width=200, height=100");
    }
}

L'interface Binder étend UiBinder qui prend deux paramètres de type, le composant de base de notre interface (VerticalPanel dans notre cas, mais nous utilisons une super classe), et la classe qui va contenir les composants reliés (La classe actuel). Nous créons ensuite, en variable static, une instance de cette interface en utilisant GWT.create(); C'est le compilateur de GWT qui s'occupera d'implémenter cette classe en utilisant les informations du fichier XML.

Puis nous ajoutons les champs de notre interface en utilisant l'annotation @UiField pour indiquer à GWT qu'il faut relier ces champs avec les composants GWT définis dans le fichier XML. Le nom du champ doit être celui donné dans l'attribut ui:field. Dans notre constructeur, nous appelons la methode createAndBindUi sur notre interface Binder, qui va alors créer et relier les composants GWT à notre classe. Enfin, la methode onClick, appelée lors du clic sur l'image, est reliée à notre composant miniature grâce à l'annotation @UiHandler("miniature").

UiBinder permet aussi de créer une interface en HTML et d'y intégrer nos composants GWT sans passer par les traditionnels composants Panel de GWT (VerticalPanel, Grid, ...), tout simplement en utilisant le composant HTMLPanel. Utilisons ce procédé pour refaire le formulaire de recherche :

<?xml version="1.0" encoding="UTF-8"?>
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
    xmlns:gwt='urn:import:com.google.gwt.user.client.ui'>
    <ui:style>
        .label {
            float: left;
            width: 50px;
            clear: both;
        }
        .element {
            float: left;
            width: 250px;
        }
</ui:style>
    <gwt:HTMLPanel>
        <dl>
            <dt class='{style.label}'>
                <ui:msg>Topic :</ui:msg>
            </dt>
            <dd class='{style.element}'>
                <gwt:TextBox ui:field="saisieSujet" />
            </dd>
            <dt class='{style.label}'>
            </dt>
            <dd class='{style.element}'>
                <gwt:Button ui:field="boutonAjouter">
                    <ui:msg>Search</ui:msg>
                </gwt:Button>
            </dd>
        </dl>
    </gwt:HTMLPanel>
</ui:UiBinder>

Ici, la classe Java est sensiblement équivalente à la classe Miniature.java :

public class FormulaireRecherche extends DialogBox {
    interface Binder extends UiBinder<Widget, FormulaireRecherche> {}
    private static final Binder binder = GWT.create(Binder.class);

    @UiField Button boutonAjouter;
    @UiField TextBox saisieSujet;

    public FormulaireRecherche() {
        super(true);
        add(binder.createAndBindUi(this));
        center();
    }

    @UiHandler("boutonAjouter")
    public void onClick(ClickEvent event) {
        hide();
    }
}

Références :

lundi 3 mai 2010

AndroidCampParis 1

Ce samedi 17 avril a eu lieu le 1er Android Camp Paris organisé par Sylvain Maucourt de Deveryware.

Il y avait certes eu un Android-"Dev"-CampParis1 mais cette fois-ci, le barcamp se voulait plus général.
Nous étions 3 à de Sfeir à participer à cet événement.

Le barcamp a commencé de manière détendue autour de pizzas offertes par Deveryware. Cela a été l'occasion de faire connaissance avant de rentrer dans le vif du sujet. Parmis la trentaine d'affamés, il y avait entre autre Jean Luc de jbmm, Pierre-Yves ou encore les fondateurs du site Standroid... et bien d'autres personnes encore. Chacun a pu sortir son appareil, le comparer, donner ses impressions...
Jean Luc, testeur pour Archos, nous a montré un nouveau firmware "3D" pour archos. Un jeu de course de vaisseaux type Star Wars I a servi de démonstration. Le contrôle se faisant par l'accéléromètre : incliner à droite ou à gauche pour tourner. Le jeu n'est pas évident à prendre en main, il faut certainement de l'habitude. Quant aux performances graphiques, elles étaient tout à fait honorables. Nous avons vu aussi voir un htc desire, petit frère que Nexus One qui ne diffère que la Sense, la surcouche de HTC.

Une fois le ventre bien rempli, nous avons commencé le barcamp proprement dit de façon traditionnelle : présentations à tour de rôle puis établissement du programme pour l'après midi. Il y avait des profils très différents : le bloggeur, le développeur qui fait de l'android quotidiennement, le développeur amateur ou même le néophite qui vient voir si android serait un bon investissement technique pour sa société.
Certains sujets se dégagent par rapport à d'autres comme le market (certaines personnes présentes étudient l'idée de lancer un market alternatif dénommé Appoke) et html5. Les sujets ont été séparés en 2 catégories correspondant aux 2 salles à notre disposition même : développement et hors-développement. Cela n'a pas empeché certains de faire des aller-retour entre les 2 salles.
En ce qui nous concernent, nous avons assisté au sujet développement.
Voici un aperçu des thèmes abordés :

Performances

Un retour d'expérience de Pierre-Yves conseille Jackson pour la génération de JSon, le gain de performance serait énorme.
Les nouveaux devices comme le Nexus One sont très performant. Quelqu'un nous parle par exemple de son application qui marche très bien sur son Nexus, mais qui très lente sur le HTC Hero d'un ami à lui. Celà peut être un danger, car on ne se rend pas forcément compte des performances si l'on teste sur son Nexus. C'est pourquoi, certains testent sur leur "vieux" Magic.
Le cycle de vie des Activity est sujet à problème pour certaines personnes. En particulier, la rotation de l'écran qui provoque la perte des données. Des solutions sont abordées allant des choix de conception à des astuces permettant de sauver des données (voir les tutoriels disponibles ici).
Le débat sur l'optimisation a été nuancé. Il faut savoir trouver un juste milieu entre recherche de la performance et maintenabilité. L'augmentation des performances des nouveaux processeurs ainsi que l'éventuel JIT de Froyo, permettront de s'autoriser plus de design pattern java classiques.

Industrialisation et outils

Pierre Yves nous parle de Roboguice, qui apporte la possibilité de faire de l'injection de dépendances sur la plateforme android. Son utilisation a un certain surcout, peu génant dans beaucoup de cas. Il permet notamment de s'affranchir de code n'apportant que peu de plus-value comme la récupération des composants déclarés dans les layouts.
Peu de développeurs présents ont exploré le terrain de l'industrialisation, signe qu'il y a encore de quoi faire de ce coté là.

WebApp, HTML, GWT

Nous avons résumé ce qui avait été dit au HTML 5 Meetup sur les "device API". Ces API interessent beaucoup les developpeurs mobiles alors que le premier SDK de l'iphone, pourtant orienté web, était un échec. Etait-ce trop tôt ?
Les apports de GWT dans le développement mobile ont aussi été abordés. On a évoqué "Modding" l'api open-source de SFEIR qui permet de faire du développement GWT pour mobile.

Market

Alexandre nous parle de son api open source : android-market-api qui permet entre autre de rechercher sur l'android market en fonction de critères comme mots clés, commentaire, ... Pour celà, il nous explique comment il a rétro-ingénieurer les échanges faits avec le market. Derrière une compression gzip et un encodage en base64, des chaines de caractères était lisibles. Le reste était basé sur protobuf.
Bien que les échanges soient optimisés, il reste tout à fait possible de les lire.
Nous nous sommes interrogés sur la stratégie de Google au sujet de ce market. Google a fait le sien, celui de référence, d'autres seraient tout à fait possible.

Fragmentation

Le problème de la fragmentation du marché est abordé. D'après les derniers chiffres, les devices en 1.5, 1.6 et 2.0 représentent chacun un tier du marché. Il est donc bien difficile d'ignorer ces anciennes versions qui représentent une part importante.
Nous parlons aussi rapidement de Froyo, la version 2.2 de Android, qui sortirait en mai et de ce qu'elle pourrait apporter.

Palm

Bien que ce ne soit pas de l'android, ceux qui avaient une expérience sur le developpement WebOS ont donner leur retour. Arrivée tardive hors des états unis, stratégie du constructeur mal comprise, difficulté à attirer des développeurs. Certains aspects comme la protection des données personnelles protègent plus l'utilisateur au détriment des possibilités pour le développeur.

Conclusion

C'est vers 18h30 que la journée se termine ....
Je crois que tout le monde est reparti des infos intéressantes, des anectodes ou des astuces de développement.
Bref une journée enrichissante !

Nous remercions encore Sylvain et sa société pour l'organisation de cet événement.



Par Alexandre et Nicolas.

lundi 5 avril 2010

GWT token Gestion de l'historique

La Gestion de l'historique et la possibilité d'avoir des pages 'bookmarkable' est un plus de GWT.
Cela est rendu possible grâce à une chaîne de caractères nomée token, placée après un # dans l'url.
Exemple :
    http://www.google.com/moderator/#15/e=8ea&t=8ea.41&f=8ea.3070

Le token représente un état du client, il n'est jamais envoyé au serveur et son changement ne provoque pas de rafraichissement de la page ni appel au serveur.

Pourquoi c'est important ? 

Les utilisateurs internautes ont acquis des reflexes "web" et s'attendent donc à :
- pouvoir faire précédent et suivant
- créer un favori pour revenir au même endroit (ou dans le même état) ultérieurement

Pourquoi c'est (encore plus) important ? 

Parce que google nous prépare une technique d'indexation des pages dynamiques

Google fait ses essais avec le Showcase de GWT.
http://www.google.fr/search?q=gwt+showcase+radio+button

Vous remarquerez le !Cw

En pratique, cela revient à fournir 2 versions de la page, une en html+Javascript pour l'utilisateur , qui veut que ça aille vite, et une html pour le robot d'indexation, qui veut lire du html.
C'est un énorme enjeu pour Google, qui a proposé un outil qui lui a 'masqué' une partie du Web.
Cette lacune est sur le point d'être comblée.

Que propose GWT ?

Les classes de base sont :
History auquel on peut ajouter un ValueChangeHandler<String> pour être prévenu des changements de token.
Hyperlink qui est le widget de base qui permet de déclencher un changement de token sur un clic.

GWT vous permet de construire votre token comme vous le souhaitez et cela reste assez bas niveau : il se contente de fournir un String java, à vous de le découper.
On remarque cependant que beaucoup adoptent les mêmes syntaxes proches des urls en utilisant les séparateurs / ? et &

Pour plus de détails : 

Frameworks
Il existe donc plusieurs frameworks qui tentent de faciliter la gestion de ce token :

Restful-gwt

http://code.google.com/p/restful-gwt/
Ce projet utilise les annotations JAX-RS afin d'extraire et découper le token.
@GET
@Path("hello/{name}")
public void helloWithIntegerParam(@PathParam("name") Integer myName) {
   
Window.alert("Hello "+(myName+1));
}

Exemple

gwt-mvc

 http://code.google.com/p/gwt-mvc/
Ce projet reprend les conventions JSP.
Le token est ainsi découpé: #ACTION?param1=value1&param2=value2
content.clearAndAdd(pocViewNumeric);
Integer modelAParamValue = Integer.valueOf(getUrlParam("modelA"));
updateModel(modelA, modelAParamValue, event);


Exemple

lundi 23 novembre 2009

La conférence "Innovation WEB" fait son retour

Le 4 décembre 2008, l'association GET (Google EFREI Technologies) organisait en association avec SFEIR une conférence sur les technologies Google.

Fort de son succès, une deuxième édition est programmée pour le 11 décembre 2009.

Cette année, il sera question d'Android, de Google App Engine, de GWT, de Google Wave et des standards du WEB (HTML5, CSS3, SVG).

Les intervenants de cette année seront Ludovic Perrier, Didier Girard et Patrick Chanezon.

Les conférences se tiendront de 13h30 à 16h30 dans les locaux de l'EFREI.


A la fin de la conférence aura lieu un cocktail, ce dernier sera suivi d'un atelier de développement Android organisé en association avec le Paris GTUG.

Pour plus d'informations, rendez vous sur le site de l'association.

jeudi 1 octobre 2009

Accélérez vos applications GWT avec Google Chrome Frame

Il y a un peu plus d'une semaine, Google annonçait la sortie de Google Chrome Frame.
Vous en avez surement déjà  beaucoup entendu parler.

Pour rappel, il s'agit d'un plugin s'intégrant à Internet Explorer et permettant de bénéficier
du moteur de rendu de Google Chrome, lui même basé sur WebKit.

Chrome Frame ne vient pas complètement remplacer le moteur de rendu d'Internet Explorer, il ne prend la main que si l'utilisateur ou la page qu'il visite le demandent explicitement.



Si vous souhaitez spécifier que votre page doit être chargée par Chrome Frame, il vous suffit de rajouter la balise suivante :

<meta http-equiv="X-UA-Compatible" content="chrome=1">


Sur le plateau de développement où je suis actuellement en mission, nous développons des applications GWT. Il s'agit d'applications de gestion dont les traitements côté client peuvent parfois être assez importants. La fluidité sous Internet Explorer n'est donc souvent pas optimale quand on la compare à celle sous Firefox ou Chrome.

Malheureusement, comme dans de nombreuses entreprises, le navigateur installé sur les postes de nos utilisateurs est Internet Explorer et faire installer un second navigateur pour optimiser la consultation de nos applications n'est pas vraiment une possibilité.

A l'annonce de la mise à disposition de ce plugin, nous avons décidé de le tester sur nos applications et les résultats se sont montrés vraiment impressionnants. Les gains en termes de rapidité d'exécution et de fluidité sont tels que l'adoption a été quasiment immédiate par le client.
Certes, tout n'est pas rose, nous avons eu quelques petites surprises d'affichage et le plugin est encore très jeune mais globalement pour un effort vraiment minime, les gains en termes de performance sont au-delà de ce que n’importe quelle optimisation de notre application aurait pu donner.


Le site officiel de Google Chrome Frame
L'annonce sur le site de GWT
Chrome Frame et Google Wave

mercredi 8 avril 2009

Développez en Java vos applications Google App Engine :

Il y a à peu près un an, Google ouvrait l'App Engine aux développeurs, il n'était alors possible de déployer que des applications développées en Python.
Pour rappel, Google App Engine est une offre de Cloud Computing permettant aux développeurs d’héberger leurs applications Web sur les serveurs de Google. Les applications ainsi hébergées bénéficient de ce fait de la même capacité de montée en charge que les services Google tels que Gmail ou Google Finance.
Google n’est pas la seule entreprise positionnée sur ce secteur, bien que pas tout à fait similaires, Amazon et Microsoft ont eux aussi leurs offres avec respectivement AWS et Windows Azure.
Un des atouts de Google App Engine c’est que gratuitement un développeur peut déployer une application qui soit accédées à raison de 5 millions de pages vues par mois. Ce qui est somme toute une offre d’hébergement intéressante. Au-delà de ces quotas, il est possible d’acheter des ressources supplémentaires.
Depuis quelques mois, nous pouvions voir dans la roadmap de ce projet que le support d’autres langages était un des objectifs de Google. Aujourd'hui, c’est chose faite, Google ouvre son service à une nouvelle communauté et contrairement à leur poisson d’avril, ce n’est pas Fortran mais bien Java qui est désormais supporté.
Cette nouvelle devrait donc être accueillie avec un vif enthousiasme lorsque l’on sait que le support de Java a été l’une des premières évolutions suggérées par les développeurs lors de la sortie de la première version de l’App Engine.
La version Java ne déroge pas au qualificatif de « Simple d’utilisation » qui caractérisait la version Python. En effet, il n’y a pas de différence majeure dans l’approche qu’ont ses deux dernières. Un atout supplémentaire de la version Java et qui a son importance, c’est le fait qu’elle s’accompagne d’un plugin Eclipse. Il est ainsi possible de créer une application et de la déployer en quelques clics.
De plus, l’intégration avec GWT (Google Web Toolkit) est enfantine et proposée par défaut par le plugin.
L’environnement de développement App Engine Java supporte les versions Java 5 et Java 6, sachant que sur les serveurs distants c’est la 6 qui est supportée. Les pré-requis au développement et au déploiement d’applications sur la plate-forme sont relativement faibles : avoir installé Java et le SDK de l’App Engine. L’installation du plugin Eclipse est bien entendu un plus non négligeable.
L’administration des applications se fait via la même interface que pour les applications Python. Il est ainsi possible de voir la répartition de la charge dans le temps, les requêtes HTTP les plus récurrentes, le taux d’utilisation ramené aux quotas. Il est également possible de consulter les logs. Enfin, une page permet d’accéder aux données stockées dans le Datastore.
Etudions de plus près l’architecture d’un projet et l’utilisation des APIs

Passons à la pratique : 

Les applications Google App Engine doivent être structurées en respectant l’arborescence WAR, ainsi, si vous créez un projet via le plugin Eclipse, vous obtiendrez une structuration semblable à celle-ci :

Cette arborescence de projet ne présente pas de spécificité particulière, on notera la présence d’un fichier web.xml permettant d’assurer le mapping entre les URLs et les classes des Servlets.
Lorsque vous déployez une application sur Google App Engine, une association s’opère entre les fichiers que vous transférez sur les serveurs de Google et les applications que vous êtes en mesure d’administrer. Cette association est possible grâce à un identifiant que vous vous devez de retranscrire dans le fichier appengine-web.xml. Ce fichier est l’équivalent du fichier app.yaml de la version Python de l’App Engine.
Les Servlets sont tout à fait communes et ne différent pas de celles que l’on peut développer dans une application traditionnelle. La Servlet d’un HelloWorld pourrait donc se présenter ainsi :

package com.sfeir.demo;

import java.io.IOException;
import javax.servlet.http.*;

public class DemoServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}


Maintenant que nous savons qu’il n’est pas difficile de créer un projet, intéressons nous aux APIs spécifiques de Google App Engine.

Utiliser les APIs :

Authentification

Ajouter un système d’authentification des utilisateurs se fait tout aussi facilement qu’avec la version Python :

package com.sfeir.demo;

import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;

public class DemoServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
              throws IOException {
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();

        if (user != null) {
            resp.setContentType("text/plain");
            resp.getWriter().println("Hello, " + user.getNickname());
        } else {
            resp.sendRedirect(userService.createLoginURL(req.getRequestURI()));
        }
    }
}


Ici, on utilise le UserService pour récupérer l’utilisateur actuellement connecté à l’application. S’il y a bel et bien un utilisateur authentifié, nous lui affichons un message de bienvenue et si ce n’est pas le cas, l’API nous permet de le rediriger vers une page de login. Lorsque vous travaillez en « local », le système d’authentification est émulé, il n’y a pas de vérification à proprement parler de l’existence des utilisateurs. Lorsque vous déployez votre application, le UserService se base sur les Google Accounts pour vérifier l’existence des utilisateurs.

Il est également possible de savoir si l’utilisateur actuellement authentifié est un administrateur de l’application et ainsi de lui donner accès ou non à certaines fonctionnalités.

Persistance


Attention, comme pour la version Python de Google App Engine, le stockage des données ne s’effectue pas dans une base de données relationnelle. Google dispose de son propre formalisme de stockage des données qui s’apparente à une Map multidimensionnelle partagée.
Le développeur dispose de trois solutions distinctes pour persister ses données, en effet, Google App Engine Java implémente JDO (Java Data Object), JPA (Java Persitance API) et met également à disposition une API de plus bas niveau (Datastore API).
Dans cette version de Google App Engine, Google pousse un Framework de persistance : DataNucleus. Sa configuration se fait très simplement (voir doc pour plus de détails)
Voici un exemple utilisant JDO, la définition des données à persister se fait par annotation.
Ci-dessous, l’objet Comment est défini comme pouvant être persisté, en le sauvant, les éléments annotés comme @Persistent feront parti de la sauvegarde.

package com.sfeir.demo;

import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.users.User;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Comment {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private User author;

    @Persistent
    private String content;

    @Persistent
    private Date date;

    public Comment(User author, String content, Date date) {
        this.author = author;
        this.content = content;
        this.date = date;
    }

}

Il est ensuite très simple de manipuler cet objet au travers de l’API. Ainsi, pour persister une instance de cet objet, on peut utiliser le PersistenceManager de la manière suivante :

Comment comment = new Comment(user.getNickName(), content, new Date()) ;
PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");
      PersistenceManager pm = pmfInstance.get().getPersistenceManager();
        try {
            pm.makePersistent(comment);
        } finally {
            pm.close();
        }



Avec ce PersistenceManager, il est tout aussi simple de récupérer un objet pm.getObjectById(…) ou encore de le supprimer pm.deletePersistent(…).
Au même titre qu’il était possible d’interroger la base au travers du GQL dans la version Python, il est ici possible d’utiliser un langage de requête proche de SQL pour récupérer des données, il s’agit de JDOQL.
On pourrait récupérer la liste de nos commentaires ainsi :

PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

PersistenceManager pm = pmfInstance.get().getPersistenceManager();

String query = "select from " + Comment.class.getName();
List<Comment> comments = (List<Comment>) pm.newQuery(query).execute() ;


Gestion d'un cache mémoire


Afin de ne pas surcharger le Datastore avec des requêtes récurrentes, il peut être intéressant de stocker un certain nombre d’informations dans un cache mémoire. Cela peut se faire très simplement au travers de l’utilisation de la Memcache API.

cache = CacheManager.getInstance().getCacheFactory().createCache(Collections.emptyMap());
cache.put(‘‘cachedInteger’’, 25) ;
Integer cachedValue = (Integer) cache.get(‘‘cachedInteger’’);



Dans l’exemple ci-dessus, après avoir récupéré l’instance du cache mémoire, on y insère un entier à la clef « cachedInteger » au travers d’un put. Un simple get avec la clef nous permet de récupérer la donnée que nous avions mise en cache.
Le cache est utilisé pour cet exemple de manière très simpliste mais on pourrait imaginer l’utiliser pour retourner des informations ayant plus de valeur, comme le contenu RSS de l’application ou encore une page fréquemment requêtée.
Cette API de cache propose d’autres fonctionnalités intéressantes comme la possibilité de définir une période de validité pendant laquelle les données peuvent être considérées comme « fraiches ». Différentes politiques de gestion sont également applicables, ainsi il est possible de n’ajouter la donnée que si celle-ci est déjà présente ou réciproquement.

Envoi de mail


L'envoi de mail fait partie des fonctionnalités intégrées à Google App Engine et relativement simples à mettre en place au sein d'un projet.
Dans cette version Java, l'API implémente l'interface JavaMail, pour des raisons de sécurité, seules les adresses d’un administrateur de l’application ou celle de l’utilisateur couramment authentifié peuvent être spécifiées comme adresses de l’émetteur.
       

Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        String msgBody = "Contenu du mail";

        ...
        Message msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress("admin@mondomaine.com"));
        msg.addRecipient(Message.RecipientType.TO,new InternetAddress("destinataire@sondomaine.com", "Destinataire"));
        msg.setSubject("Email envoyé par Google App Engine");
        msg.setText(msgBody);
        Transport.send(msg);


L’exemple ci-dessus montre l’envoi d’un mail basic, sachez qu’il est également possible d’ajouter des pièces jointes (voir types MIME autorisés) avec la contrainte toutefois que l’e-mail ne dépasse pas les 1mb.

Comme pour la version Python, il est également mis à la disposition des développeurs une API permettant d’interroger des hôtes distants (via l’API URL FETCH) et une autre permettant de manipuler des images.

Cette version de Google App Engine étant toute récente, il va être intéressant de suivre l’adhésion des développeurs de la communauté Java à ce service. D’une part, elle offre une grande facilité de développement et de ce fait un gain de temps dans l’élaboration d’applications, ce qui devrait en attirer plus d’un. D’autre part, tout comme pour la version Python un certain nombre de quotas et de restrictions sont à prendre en compte lors de l’utilisation de ce service, ce qui exclu certain types d’applications de cette offre d’hébergement.

Cette nouvelle version est également un beau coup de pousse donné à GWT, en effet le plugin Eclipse permet de créer en quelques clics une application GWT pour Google App Engine et devrait donc  amener davantage de développeurs à adopter ce Framework Web.

Google Guice sera peut être un autre des bénéficiaires de cette version Java, bien qu’il ne soit pas poussé outre mesure par Google pour être associé à Google App Engine, sa légèreté et sa simplicité d’utilisation devrait lui permettre de s’intégrer facilement aux applications déployées sur la plate-forme.

Quoi qu’il en soit c’est une nouveauté que nous nous devons de suivre.

Références :

Pour s'inscrire
L'annonce officielle
Le Coud Computing
Tout sur Google App Engine
DataNucleus
Découvrir GWT
En savoir plus sur GUICE
Amazon Web Services
Windows Azure

samedi 28 mars 2009

Chrome experiments, un sacré coup marketing

googleExperiments Google a trouvé un moyen très efficace d’imposer son navigateur. Un petit site très sympa proposant un tas de démos plus impressionnantes les unes que les autres et… nécessitant Chrome bien sûr. En effet elles reposent toutes ou presque sur le HTML 5 Canvas qui n’est pas supporté par tous les navigateurs avec la même fidélité.

Il s’agit ni plus ni moins d’une publicité comparative. Le site est là pour comparer les moteurs javascripts des différents navigateurs et c’est Chrome qui obtient les meilleurs résultats. Bien sûr, le site affiche son affinité. Mais force est de constater l’efficacité de la chose. Et puis c’est quand même plus drôle qu’une série de graphiques comparatifs.

Le site sort le même jour que le nouveau navigateur de Microsoft, IE8. La guerre est ouverte. Alors même qu’elle semble relancée entre Apple et Microsoft. Google tenterait-il d’en profiter ? Quand les arguments entre Mac et PC ne varient pas : “ils sont trop chers” contre “ils nous pillent”. Google apportera-t-il un peu de fraîcheur ? En tout cas on ne va plus savoir où donner de la tête.

Ces arguments semblent avant tout montrer que c’est la crise pour tout le monde. Quand il y avait de la place pour tous, les arguments étaient moins trash. Maintenant, on ne va pas bouder son plaisir, ces petites campagnes de pub comparatives à l’américaine sont assez drôles en général. C’est déjà ça.

On attend avec impatience le match MacOS/Windows 7. Alors, à quand l’OS Google sur PC ?

En tout cas de bons moments de rigolades en perspective (je parle du site bien sûr). Et à tester avec vos enfants bien sûr.

Le site à visiter absolument : http://www.chromeexperiments.com/

mardi 24 mars 2009

Pourquoi Douglas Bowman quitte-t-il Google ?

stop_google Vous ne le connaissez peut-être pas mais vous avez forcément vu ses œuvres. Douglas Bowman est le Visual Design Leader de Google et le fondateur de Stopdesign. Il est entré chez Google il y a trois ans, une opportunité unique, comme il l’avoue lui même. Quelle carte de visite en effet ! Alors pourquoi quitter, au bout de trois ans seulement, une entreprise en plein boom ? Et quelle entreprise. Au delà du simple différend, une coopération de 3 ans ne s’arrête pas pour rien. Trop long pour être une erreur de casting, trop court pour tenir de l’envie de “voir du pays”. Il n’arrête pas Google juste pour reprendre Stopdesign.

Il s’en explique dans un billet, sans amertume mais sans édulcorer la chose. Il semblerait bien que, de son point de vue, Google ne soit pas tout à fait la panacée pour les créatifs. Il faut dire que, passé l’écran d’accueil sur fond blanc, la créa ne parait pas forcément des plus recherchées. Ceci explique peut-être cela : Marissa Mayer (vice president of search products and user experience) centralise le design de Google, ne laissant que peu de marge de manœuvre aux designers de métier. Plus prosaïquement Google ne cherche pas le clinquant, ce qui se comprend vu son business model. Comme elle le dit elle-même “it’s not a party”.

Ce qui ressort du billet, c’est ce qu’il va en regretter. En somme, il va certes regretter l’environnement (personnes, locaux, nourriture gratuite, etc., c’est vrai qu’ils savent y mettre les moyens, quoi jaloux, moi ?) mais pas son métier là-bas : "I won't miss a design philosophy that lives or dies strictly by the sword of data."

Mais peut-on encore parler de design ? Au sens créatif visuel en tout cas. Ce qui ressort surtout du constat, est la main-mise des données sur toute décision prise. Rien n’est laissé au hasard (à l’inspiration ?). Tout découle d’études rigoureuses sur des données. Pas très excitant pour un créatif, on est d’accord. L’épaisseur d’un trait fait l’objet d’âpres discussions et de preuves attendues (comme dans la méthodologie “Split A/B testing”). Cela ressemble à l’aboutissement du rêve de la Business Intelligence : Quand les données sont suffisantes, les décisions ne sont plus nécessaires. L’étude des données suffit à établir le plan de route. Et des données, Google en a. Beaucoup.

Google n’en est plus à la création (visuelle s’entend) mais à l’optimisation permanente. Le poids des pages, la vitesse de chargement sont des facteurs qui passent avant l’apparence.

Comme quoi, malgré une réputation de geeks et de créatifs, ils sont surtout restés mathématiciens (avec les pieds sur terre quoi). "On the Web in general, (creating sites) is much more a design than an art…. You can find small differences and mathematically learn which is right." (M Mayer)

logo32Tout le contraire de chez Apple. En tout cas c’est l’avis de Chris Matyszczyk. Apple selon lui démontre de son côté que l’on peut marier l’ingénierie et le goût. En somme Google s’occupe des formules mathématiques, Apple du feeling. Mais quoi d’étonnant, après tout. On va acheter un Mac parce que c’est beau, sexy, valorisant et faire ses recherches sur Google parce que c’est performant et pas le contraire. Alors si vous êtes designer et que vous cherchez du travail, surtout, ne vous trompez pas de cible. Google n’est pas FaceBook. Google n’est pas Virgin. Google est l’aboutissement d’un rêve d’ingénieurs, un monde mathématisé, en équations, en algorithmes et en téra-octets de données. Un monde minéral ? Non, une projection sur un axe pragmatique. Un outil, mais quel outil. Au fond les vrai créatifs ne sont pas dans le visuel mais dans le fonctionnel (voir leur labs).

L’explication de l’intéressé : http://stopdesign.com/archive/2009/03/20/goodbye-google.html

L'analyse de Stephen Shankland : http://news.cnet.com/8301-17939_109-10201160-2.html

L’avis de Chris Matyszczyk : http://news.cnet.com/8301-17852_3-10201641-71.html

Marissa Mayer dans ses œuvres : http://news.cnet.com/8301-10784_3-9954972-7.html

Une petite bio de Marissa Mayer : http://www.iht.com/articles/2009/03/01/business/01marissa.php

Stopdesign : http://stopdesign.com/

Les Labs : http://labs.google.fr/

Les design guidelines google : http://googlesystem.blogspot.com/2008/03/googles-design-guidelines.html

jeudi 19 février 2009

Intégration de Guice avec les RemoteServiceServlet de GWT

La dernière version snapshot de Guice intégre des fonctionnalités intéressantes pour les Servlets, notamment de nouveaux Builders qui viennent du projet warp-servlet et qui permettent de configurer des mappings de Servlet ou de Filter directement dans un module. Vous pouvez obtenir les sources de l’exemple décrit dans cet article ici.

Au lieu de définir nos RemoteServiceServlet dans le web.xml (ce qui est désormais le cas avec GWT 1.6), nous allons déclarer un GuiceServletContextListener et un GuiceFilter :


 1 <?xmlversion="1.0"encoding="UTF-8"?>
2 <web-app>
3
4
<!-- Default page to serve -->
5 <welcome-file-list>
6 <welcome-filecda.html</welcome-file>
7 </welcome-file-list>
8
9
<listener>
10 <listener-classcom.sfeir.server.CdaGuiceServletConfig
11 </listener-class>
12 </listener>
13
14
<filter>
15 <filter-nameguiceFilter</filter-name>
16 <filter-classcom.google.inject.servlet.GuiceFilter
17 </filter-class>
18 </filter>
19
20
<filter-mapping>
21 <filter-nameguiceFilter</filter-name>
22 <url-pattern/*</url-pattern>
23 </filter-mapping>
24
25
</web-app

GuiceServletContextListener crée une instance d’Injector avec la méthode getInjector(), nous pouvons alors définir notre module en redéfinissant cette méthode :


 1 public class CdaGuiceServletConfig extends GuiceServletContextListener
 2 {
 3     @Override
 4     protected Injector getInjector()
 5     {
 6         return Guice.createInjector(new ServletModule()
 7         {
 8             @Override
 9             protectedvoid configureServlets()
10             {
11                 serve("/cda/echo").with(EchoServiceImpl.class);
12                 serve("/cda/spreadsheet").with(SpreadSheetServiceImpl.class);
13 ...

Nous utilisons pour cela une classe anonyme qui hérite de ServletModule : les méthodes «serve» et «filter» de cette dernière nous permettent de faire l’équivalent de huit lignes de XML pour déclarer une Servlet ou un Filter en une seule ligne de Java.

«serve» fonctionne directement avec les RemoteServiceServlet de GWT (il faut néanmoins ajouter y l’annotation Singleton), par contre les scopes Session et Request de la classe ServletScopes ne peuvent pas être utilisés : nous allons devoir hériter de la classe RemoteServiceServlet pour y ajouter notre propre Scope Session et par la même occasion ajouter un champ «injector» qui contiendra l’objet Injector créé par notre classe CdaGuiceServletConfig.

Dans notre ServletModule, nous rajoutons un appel à «bind» qui associe l’interface SpreadsheetManager à une implémentation :


 1                 bind(SpreadsheetManager.class).
2 to(SpreadsheetManagerImpl.class);

Ainsi l’appel de getInstance de l’object Injector avec SpreadsheetManager.class en paramètre renverra une instance de SpreadsheetManagerImpl.


 1 @Singleton
2 public class SpreadSheetServiceImpl extends GuiceRPCServlet implements SpreadSheetService
3 {
4
5
@Override
6 public List<String> fetchSpreadsheetList()
7 {
8 SpreadsheetManager manager = injector.getInstance(SpreadsheetManager.class);
9 ...

Par défaut, l’Injector de Guice renvoie toujours une nouvelle instance. Nous pouvons également utiliser l’annotation Singleton ou dans le cas présent notre propre annotation GwtSessionScoped afin qu’une même instance soit réutilisée, par exemple le temps d’une session.


 1                 bindScope(GwtSessionScoped.class,
2 GuiceRPCServlet.SESSION);
3 bind(GoogleService.class).toProvider(
4 SpreadsheetServiceProvider.class).
5 in(GwtSessionScoped.class);

Ainsi les champs injectés de type GoogleService seront créés une première fois puis récupérés dans la session en cours. Ce mécanisme fonctionne quand l’Injector de Guice est appelé, il est donc exclu de l’utiliser sur des champs d’une RemoteServiceServlet.


 1 public class SpreadsheetManagerImpl implements SpreadsheetManager
2 {
3 @Inject
4 private GoogleService service;
5
6
@Inject
7 @Named("user")
8 private String username;
9 @Inject
10 @Named("pass")
11 private String password;
12
13
@Override
14 public void authenticate()
15 {
16 try
17 {
18 service.setUserCredentials(username,password);
19 } ...

L’annotation Named est utile pour injecter objets de même type sans avoir à créer de nouvelle annotation. La méthode statique «named» crée cette annotation pour l’utiliser dans les instructions de binding.


 1                 bindConstant().annotatedWith(named("user")).
2 to("cappelle.florent@gmail.com");
3 bindConstant().annotatedWith(named("pass")).
4 to("*************");

Finalement, Guice permet aussi d’ajouter des fonctions transverses à nos services en «bindant» un intercepteur sur un ensemble de méthodes :


 1                 bindInterceptor(subclassesOf(SpreadsheetManager.class),
2 returns(subclassesOf(List.class)),
3 new LoggingInterceptor());

Ici, LoggingInterceptor est appliqué aux classes qui héritent de SpreadsheetManager et plus particulièrement aux méthodes de ces classes dont le retour est de type List. Les classes d’interception doivent implémenter MethodInterceptor de l’API aopalliance.

Vous aurez sans doute deviné que LoggingInterceptor fait un log des appels de méthode, la sécurité et les transactions étant les autres exemples typiques d’utilisation des MethodInterceptor.

En conclusion, Google Guice constitue une alternative intéressante à Spring pour l’injection de dépendance. Dans certains cas, on pourra également utiliser les méthodes de la classe SpringIntegration afin de réutiliser une configuration XML existante.

Liens :

mercredi 11 février 2009

Editing Google Spreadsheets rows with Google Data API and GWT

In a previous article (here), we saw how to display Google Spreadsheets content with the Google Data API Java client and CellFeed objects.

This time, the proof of concept application shown below uses ListEntry objects to fetch our worksheets content, using header names (see (1) and (2) in the following screenshot) and unique identifiers (see (3)) instead of x and y positioning system.

You will find the source code at the bottom.

The following piece of code shows how to fetch every row entries in a worksheet into a HashMap which maps each row identifier to its ListEntry object:


  1         Map<String, ListEntry> listEntries =
2 new HashMap<String, ListEntry>();
3 List<WorksheetRow> rows = new ArrayList<WorksheetRow>();
4 try
5 {
6 ListFeed feed = sService.getFeed(new URL(sheetFeedURL),
7 ListFeed.class);
8 for (ListEntry entry : feed.getEntries())
9 {
10 WorksheetRow clonedRow = new WorksheetRow();
11 clonedRow.setTitle(entry.getTitle().getPlainText());
12 clonedRow.setId(entry.getId().substring(
13 entry.getId().lastIndexOf('/') + 1));
14 listEntries.put(clonedRow.getId(), entry);
15 CustomElementCollection elementColl =
16 entry.getCustomElements();
17 WorksheetRow.CustomElement rowElement =
18 new WorksheetRow.CustomElement();
19 for (String value : elementColl.getTags())
20 {
21 rowElement.put(value,
22 elementColl.getValue(value));
23 }
24 clonedRow.setElement(rowElement);
25 rows.add(clonedRow);
26 }
27 getSession().setAttribute("listEntries", listEntries);
28 } catch (MalformedURLException e) ...

Each ListEntry object has a list of «Tag» (column names) and a HashMap which maps each tag name to its value in the current entry. Note that each column name is automatically made unique.

To update a row with a modified WorkSheetRow object, we get its associated ListEntry object and set every value of the WorkSheetRow object with the «setValueLocal» method.


  1     public void updateWorkSheetRow(WorksheetRow row) throws AuthException
2 {
3 Map<String, ListEntry> listEntries = (Map<String, ListEntry>) getSession().getAttribute("listEntries");
6 ListEntry entry = listEntries.get(row.getId());
7 for (String tag : row.getElement().keySet())
8 {
9 entry.getCustomElements().setValueLocal(tag, row.getElement().get(tag));
10 try
11 {
12 entry.update();
13 } catch (IOException e)...

Then we simply have to call the modified entry update method.

Going further

Row editing in Spreadsheets API is not limited to the ListEntry class: Indeed, there is also a ListQuery class which enables advanced selection for your Worksheets.

Listing in reverse order:

In order to list the rows in reverse order, you just have to set the «reverse» property of a ListQuery object to «true» and pass this object to the SpreadsheetService object


  1     ListQuery query = new ListQuery(listFeedUrl);
2 query.setReverse(true);
3 ListFeed feed = service.query(query, ListFeed.class);
4 for (ListEntry entry : feed.getEntries())
5 { ...

Full-text search:

For full-text search, use the «setFullTextQuery» method. Note that you can pass several strings.


  1     ListQuery query = new ListQuery(listFeedUrl);
2 query.setFullTextQuery("Florent Tue");
3 ListFeed feed = service.query(query, ListFeed.class);
4 for (ListEntry entry : feed.getEntries())
5 { ...

Structured query:

Structured queries can filter the list by a particular value to a column name. You can also set the ordering of the listing with a column name.


  1     ListQuery query = new ListQuery(listFeedUrl);
2 query.setSpreadsheetQuery("name = 'Florent'");
3 query.setOrderBy("column:name");
4 ListFeed feed = service.query(query, ListFeed.class);
5 for (ListEntry entry : feed.getEntries())
6 { ...

Useful Links:

Article related downloads:

lundi 9 février 2009

Leveraging Google Data APIs Java client library with GWT

While the Java client library for Google DATA API can’t be compiled by the GWT compiler, you can still use it in RPC Servlets with the advantage of hiding the protocol or substituting the default authentication backend with yours.

The screenshot below shows an example of a simple GWT application made up of a tree widget displaying my spreadsheets and their worksheets, and a grid widget on which appears the selected worksheet.

The source code of this demo is at the bottom.

Google Data APIs are based on Atom and RSS formats and the Atom Publishing Protocol. Since they are REST-styled, you may use them with any browser:

First we need to authenticate to the service we want to use (Google Spreadsheets in our case) with our Google account. The easiest way is to use the ClientLogin interface:

$ export TOKEN=`curl https://www.google.com/accounts/ClientLogin \
-d Email=cappelle.florent@gmail.com -d Passwd=$GDATA_PASSWORD \
-d accountType=HOSTED_OR_GOOGLE \
-d source=SFEIR-GWTSheets-1 \
-d service=wise | grep 'Auth=' | cut -d'=' -f2`

The service parameter is the Google service we want to use; in this case the «wise» value stands for the spreadsheets service. If the login is successful, the response will include a token which we will use in the next requests. Note that in some cases the service may require a CAPTCHA challenge to be done.

Then we request the list of the spreadsheets we include our token in the Authorization header:

$ curl -H "Authorization: GoogleLogin auth=$TOKEN" \
"http://spreadsheets.google.com/feeds/spreadsheets/private/full" > SpreadsheetList.xml

Querying and editing our spreadsheets this way (you would have to build PUT requests with XML) would be quite inefficient. Instead of parsing XML and building HTTP requests, you may use the Java client library which already does this for you with the SpreadsheetService class:


  1         SpreadsheetService sService;
  2         sService = new SpreadsheetService("SFEIR-GWTSheets-1");
  3         try
  4         {
  5             sService.setUserCredentials(email, password);
  6             logger.info(email + " authentication successful");
  7             getSession().setAttribute("sService", sService);
  8         } catch (AuthenticationException e)
  9         {
 10             logger.log(Level.WARNING, e.getMessage());
 11             thrownew AuthException(e.getMessage());
 12         }

The preceding piece of code is extracted from a GWT-RPC Service method:

  • The «setUserCredentials» method requests a token and stores it in the sService object, which we store in the session for the next requests.
  • If the login fails or a CAPTCHA challenge is needed, an AuthException is thrown back to the client.

Once the user is logged in, we build a feed URL with the FeedURLFactory class (see the following piece of code). Its «getSpreadsheetsFeedUrl» returns the URL for the spreadsheet list feed, which then is fetched by the SpreadsheetService object (sService).


  1         List<SpreadsheetEntry> spreadsheetEntries = null;
  2         FeedURLFactory factory = FeedURLFactory.getDefault();
  3         SpreadsheetFeed feed;
  4
  5         try
  6         {
  7             feed = service.getFeed(factory.getSpreadsheetsFeedUrl(),
  8                     SpreadsheetFeed.class);
  9             spreadsheetEntries = feed.getEntries();
 10             getSession().setAttribute("spreadsheetEntries",
 11                     spreadsheetEntries);
 12         } catch (IOException e) ...

The «getEntries» method returns a list of SpreadsheetEntry: Each of these entries contains standard Atom information (author, title, etc) and a worksheet list feed URL. However we don’t even need to build a feed URL since the «getWorksheets» method directly returns a list of WorksheetEntry.

The SpreadSheetModel is a list of Worksheet which is returned to the GWT client: the title and the dimensions of each worksheet are used by the tree and the grid widgets. The cell feed URL is also needed to fetch the content of each cell.


  1         SpreadSheetModel model = new SpreadSheetModel();
  2         try
  3         {
  4             List<WorksheetEntry> worksheetEntries =
  5                     entry.getWorksheets();
  6             for (WorksheetEntry sheet : worksheetEntries)
  7             {
  8                 model.add(new SpreadSheetModel.WorkSheet(
  9                         sheet.getTitle().getPlainText(),
 10                         sheet.getCellFeedUrl().toExternalForm(),
 11                         sheet.getRowCount(), sheet.getColCount()));
 12             }
 13         } catch (IOException e) ...

At the client side, the code that fills the tree widget is pretty simple:


 1             public void onSuccess(SpreadSheetModel model)
 2             {
 3                 TreeItem item = getItem(position);
 4                 for (SpreadSheetModel.WorkSheet sheet : model)
 5                 {
 6                     TreeItem newItem = item.addItem(sheet.getTitle());
 7                     newItem.setUserObject(sheet);
 8                 }
 9                 // remove the empty child (see populate method)
10                 item.getChild(0).remove();
11                 item.setState(true, false);
12             }

Once the user clicks on a worksheet title in the tree, the worksheet feed URL is given to a RPC method «fetchworksheetcells», which returns a list of cells: the Cell class of the Google API can’t be used as it is in the client side, so we have to copy its properties to a DTO class (WorksheetCell). «copyProperties» of the Apache Commons Beanutils library does this in a single line.


  1         List<WorksheetCell> cells = new ArrayList<WorksheetCell>();
  2         try
  3         {
  4             CellFeed feed = sService.getFeed(new URL(sheetFeedURL),
  5                     CellFeed.class);
  6             List<CellEntry> cellEntries = feed.getEntries();
  7             for (CellEntry entry : cellEntries)
  8             {
  9                 Cell cell = entry.getCell();
 10                 WorksheetCell clonedCell = new WorksheetCell();
 11                 BeanUtils.copyProperties(clonedCell, cell);
 12                 cells.add(clonedCell);
 13             }
 14         } catch (MalformedURLException e) ...

Note that worksheet feeds also have a list feed, which enables relational alike manipulation of worksheet data. Insertion, modification and deletion are very easy with the Java client, for example the modification of a cell entry needs two lines of code:


  1     cellEntry.changeInputValueLocal(newValue);
  2     cellEntry.update();

Useful Links:

Article related downloads:

mercredi 24 septembre 2008

Le G1 (Google Phone) fait parler de lui

Une news un peu geek pour annoncer la sortie prochaine (22 octobre 2008) du smartphone Google aux Etats-Unis pour un prix de 199$. Pour l'europe, il semblerait qu'il faille attendre avril 2009...

En s'attaquant à ce marché déjà fortement occupé par les l'iPhone d'Apple et les BlackBerry de RIM, Google fait encore une fois parler de lui :
Le Monde : Le Gphone veut la peau de l'iPhone
Libération : Google Phone : le G1 se dévoile en avance
Le Figaro : Le «Google phone» sortira fin octobre aux Etats-Unis
BBC : Google's Android mobile unveiled (avec une petite vidéo de démonstration)
El Pais : Es el turno de 'Google Phone'
Bizarrement il n'y a que dans mes feeds CNN que je n'ai pas lu cette info !

Il va être grand temps de se pencher sérieusement sur Android (la plateforme Java développée par Google sur laquelle repose le G1).
Pour avoir une petite idée quant aux possibilités de la plateforme Android, je vous propose de faire un tour sur la galerie du Android Developer Challenge.

mercredi 4 juin 2008

Google App Engine ouvert à tous

Il y a quelques temps, nous vous parlions ici même de Google App Engine, un projet de Google permettant  aux développeurs d’héberger leurs applications WEB sur ses infrastructures.

Les applications ainsi déployées bénéficient de la même qualité de service et de la montée en charge que l’on connaît pour des applications telles que Gmail, Google Finance, …
Le programme était officiellement ouvert à près de 10 000 développeurs depuis le début du mois d’avril.

Chez SFEIR, nous avions organisé une petite rencontre pour présenter cette technologie aux personnes le désirant. A cette occasion, Didier Girard avait porté son jeu KeyboardWarrior (application GWT) pour fonctionner sur Google App Engine. Curieux d’étudier la capacité de montée en charge de son application, il a organisé il y a une semaine environ un "load test". Les résultats ont été publiés ici.

Les retours des utilisateurs en termes de disponibilité de l’application sont diverses, aucun problème pour certains, un peu de lenteur pour d’autres. Quoi qu’il en soit l’application a tout de même supporté un pic de près de 23 requêtes par seconde pendant une minute, ce qui est plutôt pas mal pour un hébergement gratuit.

Mercredi dernier, Google a annoncé sur un de ses blogs l’ouverture de Google App Engine à tous, de ce fait vous pourrez bientôt tester et vous faire votre propre idée. Toutefois, avant de vous lancer dans la réalisation de votre première application, vous devriez peut être jeter un œil à quelques articles. En voici quelques uns qui expliquent comment faire en sorte que votre application soit scalable et comment il faut penser les applications pour Google App Engine. En effet certains concepts de cette technologie nécessitent que l’on pense autrement. C’est notamment le cas pour l’API datastore (persistance des données), pour laquelle il faut oublier les concepts des bases de données relationnelles.

Google a également annoncé quelle allait être la politique de prix pratiquée pour l’hébergement d’applications sur Google App Engine. Tout d’abord, l’offre d’entrée reste gratuite avec 500Mb de stockage et assez de bande passante et de CPU pour supporter l’équivalent de 5 millions de pages vues pas mois.

Les tarifs se présentent ensuite ainsi :

-10 à 12 cents par heure de CPU

-15 à 18 cents par GB de données par mois

-11 à 13 cents par GB de bande passante sortante

-9 à 11 cents par GB de bande passante entrante

Cette annonce est accompagnée par la mise à disposition de deux nouvelles APIs, une pour la manipulation d’images et une autre pour gérer un cache afin de diminuer les requêtes du datastore.

Je vous invite à suivre les retours des personnes qui assistent aux Google I/O sur Google App Engine, nous en saurons sûrement plus.

mercredi 28 mai 2008

GWT 1.5, c'est officiel ...

Quelques bruits ça et présageaient de la mise à disposition de la nouvelle version de GWT sous peu. C'est désormais chose faite.

Pour ceux qui ne connaissent pas cette technologie, il s’agit d’un Framework Web développé par Google. Il permet à des développeurs de réaliser des applications Web dynamiques basées sur Ajax sans écrire la moindre ligne de JavaScript. Les développements s’effectuent en effet en Java, ce qui permet de gagner un temps considérable dans la réalisation d’applications.

La version 1.5 RC 1 de GWT est officiellement disponible depuis ce matin au téléchargement. A noter toutefois qu'il s'agit d'une release candidate et non d'une version définitive.

Celle-ci était très attendue des développeurs, en effet, elle supporte désormais le JDK 5, les generics, l’autoboxing, les annotations, ...

En fait, on peut dire qu’aucune partie de GWT n’a réellement été épargnée, que ce soit en termes d’interface graphique, de compilation, d’appel RPC, tout a été amélioré.

On s’apercevra notamment que désormais, au niveau des composants graphiques, des animations sont disponibles ou encore des thèmes visuels. A noter également un effort au niveau de l’accessibilité avec le support de ARIA pour les composants standards (uniquement sous Firefox pour le moment).

Au delà des nouvelles fonctionnalités qui sont loin d’être négligeables, c'est un lot d'optimisations qui accompagne cette nouvelle version. Dans le processus de développement notamment, avec la réduction du temps de lancement en hosted mode et la diminution du temps de compilation. En termes de temps d’exécution de l’application également, avec un temps d’exécution plus court que si l’application avait été développée directement à la main en JavaScript.

Pour plus d’informations sur les nouveautés de cette version, c’est par ici .

Cette version de GWT était dans les cartons depuis quelques temps et on comprend pourquoi lorsque l’on regarde le travail qui a été accompli. Une bonne nouvelle donc pour beaucoup de développeurs, d’autant plus que Google a bien l’intention d’améliorer encore le processus de compilation.

Faciliter le développement d’applications GWT est un autre des objectifs de Google pour l’avenir avec notamment la mise à disposition d’un plugin Eclipse.

Sources :

http://code.google.com

http://www.regdeveloper.co.uk

http://www.eweek.com

samedi 24 mai 2008

Les Book search n'ont pas tué le livre papier

farhenheit1Après l'industrie du disque, moribonde du fait semble-t-il du piratage en masse, les éditeurs ont bien cru voir leur dernière heure venir après les lancements successifs par Google et Microsoft de leurs outils de recherche en ligne bibliographique et surtout de leur volonté affichée de numérisation massives et très ambitieuse.

Un an après le lancement de "Live Search Books", cinq après celui de Google Book Search, où en est-on ? Les peurs persistent. Et pourtant, le résultat commercial ressemble plutôt à un flop.

Microsoft arrête le projet "Live Search Books" seulement 18 mois après son lancement (voir The Chicago Blog). D'une part, Microsoft a suspendu son projet de numérisation massive et compte désormais sur les éditeurs et les librairies, d'autre part il a fermé le site séparé et regroupé son indexation dans le reste de ses résultats de recherche. C'est dommage car l'interface était plutôt sympa et la démarche respectueuse (en accord avec les droits d'auteur, ce qui n'est pas toujours le cas de Google, parfois en procès, en tout cas en débat)

Si le concept vous plaisait vraiment et que vous regrettez la démarche, vous pouvez acheter un book ripper, ça va chercher dans les 3000$ (ah c'est pas un scanner... maintenant, Microsoft en vend peut-être d'occasion ;-) ) et ça vous permet de copier 500 pages de l'heure! Bon courage quand même.

De son côté et après 5 ans, Google Book Search est toujours en version Béta mais continue.

L'utilisateur n'a pas été au rendez-vous. Et pourtant. Et pourtant les moyens mis en oeuvre étaient assez colossaux. Et pourtant les acteurs étaient nombreux et motivés (dont nombre d'universités). Et pourtant la finalité était assez noble : permettre un accès rapide et sûr à un ensemble toujours plus grand d'oeuvres et de textes en tout genre et notamment les plus rares. En somme, l'eldorado pour les chercheurs et autres étudiants (et pas qu'en lettres). C'est quand même autre chose que Wikipédia ! Au fond, c'est presque dommage que les Mormons ne se soient pas intéressés au projet.

Une question me viens comme ça: comment comptaient-ils s'y prendre pour les livres pop-up et les livres dépliants pour enfants ?

Bien entendu je n'ai pas résisté à la tentation, j'ai créé ma bibliothèque virtuelle sur Google Books:

http://books.google.com/books?as_list=BDWRCwGwQ5ISB5oaAsfTrARoUtZiceQw0vh5hGXbxnVWWLqFgTGk&hl=fr

Et là, la déception est tout de même au rendez-vous. En français en tout cas, mon Dieu que c'est pauvre. Pas un livre sur trois de ceux que j'ai lu (sans jeu de mot) qui s'y trouve. Au final, j'ai assez vite baissé les bras. openLibrary

Restent quelque belles initiatives:

 

Autre tentation à laquelle je ne vais pas résister: jouer avec Dynamic Links de Google (Book Viewability API). Et comme on est joueur ou pas, ça sera avec Silverlight. Peut-être dans un prochain post...