IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

[Projet] Mon projet de sérialisation Java "universelle"


Sujet :

Java

  1. #1
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut [Projet] Mon projet de sérialisation Java "universelle"
    Bonjour,

    Aujourd'hui je souhaiterais vous présenter mon projet universal-serializer (lien GitHub) pour sérialiser/désérialiser des objets Java à l'aide d'une interface commune Serializer.

    L'objectif ? Simplifier le développement. En effet, lorsque vous utilisez des méthodes pour transformer vos objets d'un format vers un autre vous :
    • Utilisez souvent plus d'une instruction
    • Effectuez des recherches dans la JavaDoc pour trouver la méthode qui convient
    • Pouvez oublier de fermer vos flux


    Cet API gère actuellement plusieurs formats :
    • Binaire, avec 2 implémentations : Sérialisation Java, Google Protocol Buffers
    • Fichier, avec 2 implémentations : CSV, Excel (XLS et XLSX)
    • Texte, avec 4 implémentations : JWT (JSON Web Tokens), XML (JAXB), Base64, Base64 URL


    Présentation de l'interface Serializer :
    • serialize(X myObject) : Y Sérialise un objet Java vers un autre format (binaire, texte, ou fichier).
    • deserialize(Y myData) : X Désérialise un format de donnée vers un objet Java (opération inverse de serialize())
    • sendTo(X myObject, OutputStream out) : void Sérialise un objet Java et l'envoi dans un flux (utile pour du développement réseaux)
    • receiveFrom(InputStream in) : X Récupère un objet Java à partir d'un flux (utile pour du développement réseaux)


    Voici un exemple d'utilisation pour chaque format :
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Sérialisation Java
    JavaSerializer<MyClass> s = new JavaSerializer<>();
    byte[] byteArray = s.serialize(new MyClass(/* init */));
    MyClass deserialized = s.deserialize(byteArray);
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Sérialisation Excel
    ExcelSerializer<MyClass> s = new ExcelSerializer<>(MyClass.class /* , your FileOptions */);
    Path path = s.serialize(new ArrayList<MyClass>());
    List<MyClass> deserialized = s.deserialize(path);
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // Sérialisation JWT
    String SECRET = "546T78UINqqsvfzfs<vs<sdv_-('U87Y89YG87";
    JwtSerializer<MyClass> s = new JwtSerializer<>(MyClass.class, Algorithm.HS256, SECRET);
    String jsonWebToken = s.serialize(new MyClass(/* init */));
    MyClass deserialized = s.deserialize(jsonWebToken);

    Questions :
    1. Que pensez-vous de ce projet ? Vous semble-t-il suffisamment intéressant pour être utilisé dans vos projets ?
    2. Quels sont les implémentations que vous souhaiteriez voir apparaitre dans l'API ? (Mis à part JSON, que je ne compte pas implémenter)
    3. Si vous aviez à changer les dépendances du pom.xml, mettriez-vous des dépendances en optional true ?

    Note : En ce qui concerne le nom du projet, j'ai pensé à une télécommande universelle


    Merci pour vos retours
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  2. #2
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Je n'ai jamais eu le besoin de serialiser à ce jour mais ca me parait interessant, oui. Par curiosité, j'imagine que tu utilises la reflexion pour serialiser. Comment geres tu les references circulaires (typiquement, la liste chainee dans les deux sens) ?

  3. #3
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut
    Oui, j'utilise la reflection. Pour le cas des formats fichier pas de souci, car les types acceptés sont basiques (des primitifs + wrapper, String et dates). Pour le format binaire comme Java c'est géré par la sérialisation Java. Pour les implémentations texte qui utilisent la librairie gson (pour le json) et jaxb (pour le xml) ce n'est pas géré il me semble.
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  4. #4
    Membre expert

    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2004
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 301
    Points : 3 675
    Points
    3 675
    Par défaut
    Tu dois implémenter une interface spécifique pour être sérializable avec ton api? donc je dois me coupler fortement à ta lib, et je ne peux rien sérialiser si je n'ai pas accès au code source de l'objet en question (genre un objet hérité d'une api sur lesquelles je n'ai aucun contrôle)... EDIT: ha non y'a pas d'interface spécifique, c'est cool

    Quel est l'avantage finalement, par rapport à la sérialisation standard de Java? J'imagine que pour produire une sortie binaire standard de mes objets, je dois implémenter ton interface + java.io.Serializable (sinon tu risques de prendre une NotSerializableException)? donc je dois respecter le contrat de serializable (implémenter Serializable, méthodes readResolve, champ serialVersionUid, etc...) donc pas de simplification de la sérialisation de ce côté

    Comment te protèges-tu contre les corruptions de flux. J'ai une classe qui détermine un invariant (genre dateDebut, dateFin, avec dateDebut<=dateFin). Si je bidouille les flux à la main, comment détectes-tu que la désérialisation doit échouer? PS: Serializable permet ça via les méthodes readObject/readResolve

    Comment adresses-tu les potentiels problèmes de compatibilité entre les versions d'une classe? Genre ma classe A en v1 n'avait pas de champ "int pouet", mais en version v2 oui: que se passe-t-il quand je désérialize un flux issu de la version 1 dans un classpath qui contient la version 2?

    Comment détermines-tu le type réel à créer? Je vois que tu passes des listes "List<MaClasse>", mes les génériques sont perdus à la compilation. Comment sais-tu que lors de la désérialisation, c'est des instances de MaClasse qu'il faut produire? Tu stockes l'information dans le flux? Dans le cas d'un fichier Excel, ça part où? EDIT: ok c'est en construisant le sérializeur, j'avais pas vu

    Pourquoi produis-tu des Path en sortie? Je n'ai aucun choix sur l'endroit où je veux stocker les infos?? Pour moi, un serialiseur doit lire/écrire des flux, pas référencer d'autres api. L'utilisateur s'occupe ensuite de brancher les flux sur ce qu'il veut (un FileIn/OutputStream, un socket.getIn/OutputStream, un ByteArrayIn/OutputStream pour avoir les données en mémoire, etc...)

    Et si tu ne résous pas la problématique des références circulaires, quel que soit le format utilisé, ça va vite perdre de son intérêt...

    Bref, pourquoi pas, mais je pense qu'il faudrait aller au fond de la réflexion pour être utilisable en prod...
    "Le plug gros problème des citations trouvées sur internet, c'est qu'on ne peut jamais garantir leur authenticité"

    Confucius, 448 av. J-C

  5. #5
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Sauf erreur de ma part (j'ai regardé le code il y a quelques jours), il utilise writeObject donc il suffit d'implémenter java.io.Serializable (il n'y a pas une autre interface "custom" à implémenter).

    Par contre, je suis d'accord qu'il faudrait etre plus clair sur l'objectif du projet. Typiquement, si l'objectif est d'avoir un outil qui permet réellement de gérer la serialisation, je trouve aussi qu'il manque encore beaucoup de choses (pas de gestion de version de ce que j'ai vu, pas de gestion de references, ...).

    Mais si l'objectif est simplement de mettre à disposition un outil qui gere des besoins basiques, pourquoi pas ? Mais dans ce cas, ce serait bien de mieux cadrer ce qui est possible ou non.

  6. #6
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut
    Quel est l'avantage finalement, par rapport à la sérialisation standard de Java? J'imagine que pour produire une sortie binaire standard de mes objets, je dois implémenter ton interface + java.io.Serializable (sinon tu risques de prendre une NotSerializableException)? donc je dois respecter le contrat de serializable (implémenter Serializable, méthodes readResolve, champ serialVersionUid, etc...) donc pas de simplification de la sérialisation de ce côté
    L'avantage, c'est la simplicité à écrire le code, et aussi de ne pas avoir à oublier la fermeture du flux (dans le cas ci-dessous la fermeture n'est pas nécessaire avec ByteArrayOutputStream, mais pour d'autres implémentations ça peut l'être) :
    vanilla Java universal-serializer
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    try (
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos)
    ) {
        oos.writeObject(objectToSerialize);
        byte[] byteArray = baos.toByteArray();
    } catch (IOException e) {
        // ...
    }
    Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    try {
        JavaSerializer<MyClass> s = new JavaSerializer<>();
        byte[] byteArray = s.serialize(objectToSerialize);
    } catch (SerializationException e) {
        // ...
    }

    Tu n'es pas obligé d'implémenter Serializable sur ton type tant que ton implémentation le fait (ex: tu peux sérialiser une List tant que l'implémentation implémente Serializable, ex : ArrayList). C'est expliqué dans la JavaDoc de JavaSerializer. Il n'a jamais été obligatoire d'implémenter les méthodes readResolve ou champ serialVersionUid dans ton type pour faire de la sérialisation Java.


    Comment te protèges-tu contre les corruptions de flux. J'ai une classe qui détermine un invariant (genre dateDebut, dateFin, avec dateDebut<=dateFin). Si je bidouille les flux à la main, comment détectes-tu que la désérialisation doit échouer? PS: Serializable permet ça via les méthodes readObject/readResolve
    Comment adresses-tu les potentiels problèmes de compatibilité entre les versions d'une classe? Genre ma classe A en v1 n'avait pas de champ "int pouet", mais en version v2 oui: que se passe-t-il quand je désérialize un flux issu de la version 1 dans un classpath qui contient la version 2?
    J'utilise les librairies Java, donc c'est la JRE qui se charge de la corruption du flux ou des problèmes de version. Cela ne t'empêche de modifier les octets si tu connais le protocole de sérialisation Java et que tu sais exactement quelles valeurs tu veux modifier.

    Pourquoi produis-tu des Path en sortie? Je n'ai aucun choix sur l'endroit où je veux stocker les infos?? Pour moi, un serialiseur doit lire/écrire des flux, pas référencer d'autres api. L'utilisateur s'occupe ensuite de brancher les flux sur ce qu'il veut (un FileIn/OutputStream, un socket.getIn/OutputStream, un ByteArrayIn/OutputStream pour avoir les données en mémoire, etc...)
    Path est le type Java standard pour représenter le chemin d'un fichier, par défaut le fichier est créé en tant que temporaire, si tu souhaites modifier l'endroit où tu veux récupérer/stocker ton fichier il suffit de le renseigner dans l'objet FileOptions#destinationPath que tu placeras le constructeur des sérialiseurs fichier (c'est indiqué dans le README.md). Enfin si tu veux manipuler des flux, tu peux utiliser les méthodes sendTo() et receiveFrom()
    Et si tu ne résous pas la problématique des références circulaires, quel que soit le format utilisé, ça va vite perdre de son intérêt...
    J'ai créé un wrapper, je lègue les problématiques aux implémentations, dans le cas actuel à JAXB et Gson.


    je trouve aussi qu'il manque encore beaucoup de choses (pas de gestion de version de ce que j'ai vu, pas de gestion de references, ...)
    Pour avoir implémenté la sérialisation Java (création d'un bridge en C#), ou utilisé d'autres formats, la gestion de version n'a jamais été une obligation quelque soit le format utilisé. Qu'est-ce que la gestion de references ?


    Merci pour vos retours
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  7. #7
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Dans nos programmes, nous avons souvent besoin de stocker des objets utilisés à plusieurs endroits (mais une meme instance). Pour cela, on garde simplement une reference sur l'objet en question. Une des difficultées de la sérialisation est de ne pas recréer tous ces objets au moment de la dé-sérialisation.

    Typiquement, je parle de ca:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class MaClasse
    {
       private List<AutreClasse> listAutreClasse = new LinkedList<AutreClasse>();
       private AutreClasse classeChoisie;
     
       public void setClasseChoisie(int index)
       {
          classeChoisie = listAutreClasse.get(index);
       }
     
       public void majList()
       {
          for(AutreClasse autre : listAutreClasse )
          {
             autre.maj();
          }
       }
     
       public void utiliseClasseChoisie()
       {
          classeChoisie.faitQuelqueChose();
       }
    }
    Ici, on voit clairement que classeChoisie est utilisé comme une référence d'un élément de listAutreClasse. Si tu désérialize, tu risques d'avoir des instances différentes ce qui peut provoquer un comportement inatendu (dans mon exemple, l'element choisi ne serait pas mis à jour en cas d'appel à majList() alors qu'on s'attendrait à ce qu'il le soit).

    Note qu'ici, on est sur un exemple assez simple mais ca peut vite devenir tordu quand c'est une instance partagée par plusieurs classes...

  8. #8
    Membre expert

    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2004
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 301
    Points : 3 675
    Points
    3 675
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    Il n'a jamais été obligatoire d'implémenter les méthodes readResolve ou champ serialVersionUid dans ton type pour faire de la sérialisation Java.
    Non tu n'es pas obligé, mais si tu veux de la sécurité, parfois, il faut... sinon tu risques plein de trucs pas marrants...

    Citation Envoyé par Gugelhupf Voir le message
    J'utilise les librairies Java, donc c'est la JRE qui se charge de la corruption du flux ou des problèmes de version. Cela ne t'empêche de modifier les octets si tu connais le protocole de sérialisation Java et que tu sais exactement quelles valeurs tu veux modifier.
    Oui, c'est justement mon problème... si je change de format, je passe en mode excel, je perd les vérifications et capacités inhérentes à la sérialisation java (ainsi que ses faiblesses d'ailleurs...)


    Citation Envoyé par Gugelhupf Voir le message
    Enfin si tu veux manipuler des flux, tu peux utiliser les méthodes sendTo() et receiveFrom()
    Justement, pour moi, ce sont les seules signatures utiles. Si je veux un fichier temporaire, je le créé moi-même et je te donne le flux qui correspond. Ton outil (dans la philosophie d'un serializer) est là pour transformer des objets en byte[] et vice versa... pas pour décider de quoi faire avec ces byte (les envoyer dans un fichier). Donc l'interface du Serializer me semble pas "pure"

    Citation Envoyé par Gugelhupf Voir le message
    J'ai créé un wrapper, je lègue les problématiques aux implémentations, dans le cas actuel à JAXB et Gson.
    C'est donc un simple wrapper autour d'autres technos... donc, tu ne caches pas la complexité de ces technos, tu fournis un toolkit pour les utiliser au travers d'une autre api, mais en gardant les contraintes inhérentes à la techno utilisée (annotation en JAXB, méthodes privées et champ de version avec la sérialisation, etc). C'est en ça que j'ai un problème, c'est pas un "universal serializer", c'est une façade (ou plutôt une série de façades qui ne partagent pas d'api uniforme) qu'on pose devant les API standards...

    Ce qui serait royal, ce serait justement de cacher tout ça: sérialiser des objets, même si il ne sont pas serializable et ne sont pas annotés, de la même façon, quel que soit le format de sortie, en fournissant des moyens uniformes de traiter les cas bizarres: quelqu'un a corrompu le flux, les versions des classes ont changées, les invariants de classe ne sont plus garantis, toussa toussa...

    Alors j'essaie pas de dénigrer le travail effectué, le code semble bon et tout et tout, par contre, l'intérêt est limité: tu ne fais rien de plus que ce qui est proposé par les implém' sous-jaçentes, tu ne fais que changer les interfaces. Et l'on sait bien, que changer les interfaces d'une API sans ajouter de fonctionnalités, c'est se priver des futures évolutions de cette API (en tout cas, ça complique la chose) sans gain réel, en plus, ça perturbe les développeurs qui ont l'habitude de faire les choses d'une certaine manière. C'est en ça que je ne pourrais pas l'intégrer dans mes projets pro...
    "Le plug gros problème des citations trouvées sur internet, c'est qu'on ne peut jamais garantir leur authenticité"

    Confucius, 448 av. J-C

  9. #9
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut
    @hwoarang,

    Sérialiser un même objet pour l'envoyer vers plusieurs flux n'est pas un cas que j'ai beaucoup rencontré, même si ça peut avoir son utilité le fait d'ajouter une couche qui à chaque opération parcours une liste (disons de weak reference) des instances est une opération supplémentaire. L'API n'apporte pas d'optimisation particulière pour le cas où un même objet est sérialisé plusieurs fois, mais rien n'empêche l'utilisateur de sérialiser son objet une première fois, puis de se resservir du résultat pour l'envoyer vers divers flux.

    @Pill_S,
    L'API est suffisamment générique pour ne pas forcer l'utilisateur à implémenter d'autres types (comme tu le souhaites pour le découplage), et n'empêche pas l'utilisateur de créer sa propre stratégie dans le type qu'il crée avec ces méthodes "magiques" de sérialisation Java (sans que moi créateur de l'API apporte ma propre stratégie qui peut ne pas te satisfaire).
    Tu te rends bien compte que pour la sérialisation Excel ou CSV tu ne pourras jamais utiliser un très objet complexe ? Quand on crée un fichier Excel, c'est pour que ce dernier soit exploitable par l'humain ("on veut juste une feuille avec un tableau contenant des données, et si possible une en-tête"), forcément tu ne vas pas y embarquer toute la complexité de la sérialisation Java dans ces fichiers qui ne sont exploités que par la sérialisation Java. Tout n'est pas liée à la sérialisation Java.
    Aujourd'hui l'API propose 8 implémentations différentes, avec une complexité variée pour chaque implémentation (celle de Java n'est pas intéressante, mais celle de JWT l'est par exemple), vouée à simplifier l'écriture du code, à rendre la lecture plus intuitive. Pour comprendre le réel avantage de cet API, je t'invite à créer un fichier Excel à partir d'une liste d'objets Java contenant une dizaine de type variées.
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  10. #10
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    @hwoarang,

    Sérialiser un même objet pour l'envoyer vers plusieurs flux n'est pas un cas que j'ai beaucoup rencontré, même si ça peut avoir son utilité le fait d'ajouter une couche qui à chaque opération parcours une liste (disons de weak reference) des instances est une opération supplémentaire. L'API n'apporte pas d'optimisation particulière pour le cas où un même objet est sérialisé plusieurs fois, mais rien n'empêche l'utilisateur de sérialiser son objet une première fois, puis de se resservir du résultat pour l'envoyer vers divers flux.
    Je comprends ta logique. Ce qui me derange, c'est plus la forme que le fond. Le nom du projet "universal-serializer" me laisse penser que ce type de probleme est abordé. Ici, on est plus sur un wrapper qui permet de simplifier le code pour utiliser des fonctions de sérialisation classique. Je pense que la doc pourrait etre plus claire sur ce que permet ou non l'API. Note que si l'objectif est de simplifier le code, je pense qu'il pourrait etre interessant de faire une (ou plusieurs) classe utilitaire qui proposerait des methodes static spécialisées permettant de sérialiser en 1 ligne.

Discussions similaires

  1. [Projet] Mon projet domotique "wagoïd"
    Par alain1968 dans le forum Android
    Réponses: 3
    Dernier message: 15/10/2011, 19h40
  2. Réponses: 9
    Dernier message: 10/01/2007, 09h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo