Month: June 2008

Séminaire Valtech “Les méthodes Agiles dans les Projets IT”

Posted by – June 29, 2008

J’ai assisté jeudi dernier matin à un séminaire proposé par Valtech sur les méthodes agiles. Les 2 premières sessions auxquelles j’ai assisté étaient dispensées par David Gageot sur une introduction aux méthodes Agiles et Hubert Gillon sur le thème des projets offshore.

Séminaire riche d’enseignements,étant un convaincu de ces méthodes.

More

Réinstallation de la station de travail, loi de Murphy et logiciels utilisés

Posted by – June 27, 2008

Selon la loi de Murphy

Si quelque chose peut mal tourner, alors ça tournera mal.

J’ai dû réinstaller ma station de travail. Je suis plutôt du genre prévoyant, et comme je le présentais, cela s’est mal passé, forcément, c’est la loi de Murphy qui le dit.

Aussi, comme toute gestion du risque, j’ai passé, avant la réinstallation, ma station (Windows 2003 serveur) en machine virtuelle afin :

  • d’avoir une copie des fichiers, des logiciels installés, et autres configurations (configuration IIS, PuTTY, …),
  • de pouvoir continuer à travailler si cela se passait mal

Pour faire cela, il existe un magnifique outil gratuit fourni par VMWare : VMWare Converter, il vous permettra de transformer votre machine en machine virtuel que vous utiliserez ensuite sous VMWare Server ou VMWare Player. Avant d’effectuer la conversion, il est conseillé de changer l’IP, ainsi que le nom de machine si elle se trouve sur un domaine, cela évitera les doublons lors de son exécution en même temps que votre machine physique.

Installation effectuée de Windows 2003 R2 Std. Ed. avec succès, les windows update compris. En plus de mes outils de travail (VStudio 2005 & 2008, VSS, SQL Manager, Firefox & extensions, …), j’utilise pleins d’autres outils qui nous facilitent la vie, une petite liste :

  • notepad++ : l’éditeur de texte ultime, ne pas oublier d’installer l’éditeur Hexa,
  • AxCrypt : un utilitaire comme j’aime : il permet de crypter vos fichiers que vous souhaitez protéger : fichiers de configurations (de putty , de vos clés privées), de mots de passe, ou vos fichiers personnels pour les curieux,
  • Xobni : un add-in à Outlook assez efficace pour la recherche, et vous donnera également tout un tas de statistiques sur vos mails et leur maillage entre vos contacts,
  • SQLPrompt : un outil pour le Query analyser de SQL Server, très utile pour la complétion lors de l’écriture de requêtes SQL,
  • Trillian : pour MSN, ICQ & co,
  • Firefox 3 et mes extensions favorites,
  • Screenshot captor : un utilitaire de captures d’écran, avec la possibilité de les annoter
  • Freemind : logiciel Opensource (en Java) pour créer des mindmap
  • FDM : logiciel de gestion de téléchargements
  • Filezilla : client FTP
  • ZipGenius : utilitaire de compression
  • Safari : le navigateur made in Apple

S’il y a des oublis, je compléterai la liste.

NHibernate et la magie des Criteria en de certaines circonstances…atténuantes ?

Posted by – June 26, 2008

NHibernate permet entre autre d’interroger les objets grâce aux requêtes Criteria, cela évite d’injecter du SQL.

Pour poursuivre sur ce billet, on va parler de l’interrogation de propriétés d’objets sous NHibernate.

Lors d’une relation many-to-one, on peut ainsi accéder à l’entité déclarée pour lire les propriétés de cette dernière, un exemple de mapping hbm qui permettra d’effectuer un appel du type abonnement.Individu.Email, un exemple de mapping hbm, qui représente la classe NewslettersAbonnements du schéma

newsletters diagramme

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >
  3. <class name="NewslettersConnector.Model.NewslettersAbonnements, NewslettersConnector" table="NEWSLETTERS_ABO">
  4. <id name="Id" column="ID" type="System.Int32" access="property">
  5. <generator class="identity"></generator>
  6. </id>
  7. <property name="IsActif" column="abo_isactif" not-null="true" type="System.Boolean" access="property"/>
  8. <property name="DateCreation" column="abo_datecreation" not-null="true" type="System.DateTime" access="property"/>
  9. <property name="TypeAbonne" column="abo_typeabonne" not-null="true" type="NewslettersConnector.Model.TypeAbonne, NewslettersConnector" access="property"/>
  10. <property name="IndId" column="ind_id" not-null="true" type="System.Int64" access="property"/>
  11. <property name="IsSynchro" column="is_synchro" not-null="true" type="System.Boolean" access="property"/>
  12. <many-to-one name="Individu"
  13. class="NewslettersConnector.Model.Individu, NewslettersConnector"
  14. column="ind_id" not-null="true" fetch="select" cascade="none" insert="false" update="false"/>
  15. <many-to-one name="Newsletter"
  16. class="NewslettersConnector.Model.Newsletters, NewslettersConnector"
  17. column="nl_id" not-null="true" fetch="select" cascade="none"/>
  18. </class>
  19. </hibernate-mapping>

Dans notre exemple, on veut sélectionner un abonnement pour une newsletter et un email d’un abonné.

Le seul problème, c’est qu’en écrivant cela :

  1. IList liste = _laSession.CreateCriteria(typeof(NewslettersAbonnements))
  2. .Add(Expression.Eq("Newsletter.Id", nlid))
  3. .Add(Expression.Eq("Individu.Email", mail))
  4. .List();

nous avions une exception, NHibernate n’arrivait pas à trouver la propriété Email, alors que l’identifiant de l’individu passait bien, l’erreur levée :

could not resolve property: Individu.Email of: NewslettersConnector.Model.NewslettersAbonnements

Mon collègue qui a un œil bionique et surtout qui sait utiliser Google nous a trouvé la raison, c’était écrit :

Comme le dit la documentation p. 93 :

(…) The following, alternate form is useful in certain circumstances. (…)

donc en certaines circonstances, le CreateCriteria a besoin d’alias pour introspecter une propriété d’une entité d’un objet en cours. Le code devient alors :

  1. IList liste = _laSession.CreateCriteria(typeof(NewslettersAbonnements))
  2. .CreateAlias("Individu","ind")
  3. .Add(Expression.Eq("Newsletter.Id", nlid))
  4. .Add(Expression.Eq("ind.Email", mail))
  5. .List();

on ne connaitra donc jamais pas les circonstances qui font qu’une exception soit levée dans ce cas…

mise à jour vers dotclear 2 RC2

Posted by – June 25, 2008

dotclear 2 RC2 est sorti, le blog a été mis à jour :

  • sauvegarde du répertoire dotclear
  • suivre les instructions suivantes
  • mettre à jour les spécificités du blog qui ne peuvent être gérées avec des extensions

Rappel : demain matin, rendez-vous à la Défense.

EDIT : bug sur la gestion des médias (sous FF3) : il n’insert plus le code dans le billet lors de la sélection d’une image

Links #4 : SCM

Posted by – June 24, 2008

Il est temps…d’abandonner VSS…

J’ai enfin trouvé : Mouse gestures pour Firefox 3

Posted by – June 23, 2008

Firefox 3.0 sorti, nous sommes tous à la recherche des mises à jour de nos extensions favorites. Mousegestures (qui ne se trouve donc pas sur le site officiel des extensions) fait parti de mes must have, quand on navigue beaucoup sur le Net, le clavier en devient presque superflu grâce à cette extension qui rend d’énormes services : loupe (grossit la police, ben oui, dépassé 30 et des bananes, les yeux en ont besoin…même quant on a 15/10…si, si, c’est possible), page de retour, ouverture d’onglets, …

On pourra aussi parler de :

Voir également les dizaines d’extensions disponibles pour les développeurs (ou pas) :

Jouons avec les alias (CNAME) DNS : google (gmail, cal, blog, …), feedburner, myopenid, un soupçon de SaaS avec son domaine

Posted by – June 22, 2008

Ce qui est de plus en plus courant maintenant, c’est que les (grands) éditeurs de solutions en ligne proposent maintenant la possibilité de devenir soi-même fournisseur de services : Google avec la messagerie, agenda, documents ou encore son blog avec Blogger, Feedburner pour garder son domaine dans l’URL, ou encore MyOpenID qui permet de créer des comptes OpenID avec son propre domaine.

MyOpenID

myopenid est un fournisseur d’identités OpenID, il propose tout un tas de service pour s’authentifier (voir ce billet), et malgré quelques coquilles de traduction, il innove souvent, ce site reste un must have pour gérer son identité OpenID.

J’ai un domaine olivier-duval.info, j’aimerais l’utiliser pour gérer mon identité (URL OpenID), à la place de http://myopenid.com/zorky par exemple. J’ai maintenant une URL OpenID de type http://myopenid.olivier-duval.info/od : permet d’ajouter des comptes (sur http://myopenid.olivier-duval.info/<utilisateur>) et de garder son nom de domaine.

Comment faire ?

Simple : créer une entrée CNAME sur votre zone :

myopenid CNAME www.myopenid.com.

avant CNAME vous mettrez le nom que vous souhaitez, cet enregistrement donnera myopenid.olivier-duval.info me concernant.

Après l’ajout d’un nouveau domaine à gérer, MyOpenid demande une phase de vérification afin de vérifier que ce domaine vous appartient bien : soit une entrée spéciale dans la zone DNS, soit une simple page HTML avec un code qui sera lu, j’ai choisi cette dernière.

Myopenid se présente ainsi :

Gestion du domaine (ici myopenid.olivier-duval.info)

Myopenid domaine

Méthode de vérification du domaine

Myopenid vérif domaine

Une fois le domaine configuré, vous pourrez y ajouter autant de comptes que vous le souhaitez. Pour l’instant Myopenid n’offre pas d’API afin de gérer tout ça directement à partir d’une application tierce, ce qui serait d’autant plus pratique que de le faire par l’interface Web myopenid.

Google

Google apps vous permettra de fournir des services à vos utilisateurs, leur ouvrir des comptes pour gérer leur email, leur calendrier, leur page web, ou leur blog.

2 billets sur le sujet :

En plus de zorky00 AT gmail.com, je gère mon domaine et ses utilisateurs sur http://webmail.olivier-duval.info, je peux ainsi créer autant de mail que je le souhaite, ou encore http://calendar.olivier-duval.info, etc.

Feedburner

On ne parlera pas ici de SaaS mais simplement la capacité à utiliser votre domaine pour l’abonnement au flux, nom de domaine qui reste votre marque, aussi, je préfére avoir feeds.olivier-duval.info/OlivierDUVAL que feeds.feedburner.com/OlivierDUVAL.

Simple, il suffit de rajouter dans votre zone (olivier-duval.info par exemple) un CNAME de la façon suivante :

feeds CNAME feeds.feedburner.com.

Vous pourrez ainsi faire pointer votre flux sur http://feeds.olivier-duval.info/OlivierDUVAL

J’espère que de plus en plus d’éditeurs offriront ce type de gestion transparente, devenir fournisseur de services à moindre frais, un réel service !

Todo : Test de Basecamp

Posted by – June 19, 2008

Tout bon CP ou développeur (cela n’engage que moi…) se doit de gérer une todolist (j’utilise parfois TadaList mais non adapté pour un travail en mode collaboratif), soit sous forme d’un fichier ou sous forme de post-it, cela permet d’avoir une vision assez globale de ce qu’il reste à faire sur un projet, comme ça, on diminue le risque d’oublis. Le seul problème de cette méthode (post-it, excel, …), c’est que lorsqu’on travaille à plusieurs sur un même sujet, tous les membres n’ont pas la même vision de ce qu’il reste à réaliser, au pire, il reste le risque de faire la même chose plusieurs fois, sans compter qu’un post-it s’égare ou se jette.

Je suis en train de tester basecamp (rendu célébre par la fameuse entreprise Web2 37signals, vous savez Ruby On Rails…), qui permet gratuitement d’ouvrir un compte pour une entreprise, lui associer au moins un projet et permet ainsi de profiter de liste de tâches et milestones (jalons ou versions en français). Basecamp propose divers liens, de type http://entreprise.updatelog.com, avec une gestion de comptes (membres d’une équipe par exemple), ainsi qu’un projet, et les todolists par chantier dans ce dernier.

Premières impressions de basecamp :

  • ergonomique et prise en main très rapide, va à l’essentiel, et sait intelligemment tirer parti d’Ajax
  • implémente OpenId
  • la version gratuite répond à mes besoins nominaux : gérer des tâches et les barrer au fur et à mesure de la réalisation

Pour la todolist, même si on peut affecter un membre à une tâche (même si ça peut en démanger parfois certains), il reste préférable que chacun s’affecte quelque chose après discussion, l’implication sera d’autant plus grande qu’une tâche imposée.

Quelques images écran de basecamp

Accueil tableau de bord :

accueil Basecamp

Todolist d’un projet :

Basecamp project todolist

Les milestones :

Basecamp milestones

Personnalisation de son compte :

Basecamp myinfo

Petit défaut : lors d’une échéance programmée pour une version (milestones), le nombre de jours restants est en jours ouvrables, donc à moins de travailler le week-end, c’est un peu gênant.

Intéressant en tout cas, et assez adapté également, expérience à poursuivre.

ORM / NHibernate : mapper des sous-collections ou pas ?

Posted by – June 17, 2008

Préambule

Actuellement, je gère un projet de mutualisation de newsletters en collaboration avec un membre de l’équipe. Celles-ci seront intégrées à notre plateforme extranet, les abonnés gérés par des propriétaires. Les abonnés sont soit extraits de l’annuaire interne à notre extranet, soit externes à ce dernier et donc ajoutés manuellement.

Afin de gérer ce cas d’utilisation, nous aurons notamment besoin de connaître :

  • tous les abonnés,
  • les abonnés internes, issus de l’annuaire,
  • les abonnés externes, ajoutés manuellement par le propriétaire

On aura schématiquement le diagramme de classes suivant (merci à Dia) :

diagramme newsletters

Question philosophique du jour

Pour gérer les abonnements, nous nous appuyons sur un contrôleur (ci-après), et j’ai eu un doute sur la méthode pour obtenir les sous-ensembles d’abonnés internes et externes : utiliser la capacité de l’ORM de le faire ou interroger le contrôleur (voir l’introduction aux architectures n-tiers) avec les méthodes adhoc ?

Explications

NHibernate, l’ORM que nous utilisons, permet de créer des collections d’objets avec des clauses where (et donc d’avoir des sous-ensembles d’objets de même type), dès lors, plus besoin d’avoir des méthodes (par exemple List<NewslettersAbonnements> listAbonnementsExternes(int nlid) et List<NewslettersAbonnements> listAbonnementsInternes(int nlid))) dans le contrôleur pour obtenir ce type de collections.

Après discussion avec les membres de l’équipe, nous sommes arrivés aux arguments suivants :

  • pour plus de clarté, la classe sérialisée par NHibernate devra contenir les commentaires qu’il faut (ie : comment sont générées ces collections, pour expliquer que ce n’est pas magique),
  • cela concerne des collections précises et peu d’objets,
  • très certainement (mais en était-on vraiment sûr ? nouvelle question philosophique), il n’y aura pas de service Web pour l’accès au contrôleur. S’il avait été question d’un service Web, dans ce cas, il y aurait besoin de méthodes, en raison du souci de sérialisation des collections IList (en .NET, la sérialisation par défaut des WS s’effectue par le provider XmlSerializer, d’où les XmlIgnore dans la classe d’après sur les propriétés IList),
  • le fait de créer des méthodes spécifiques pour obtenir les abonnements internes et externes apporte peu de valeur ajoutée et que cela surchargerait le contrôleur par des méthodes qui ne sont pas liées directement au cas d’utilisation,
  • on s’appuie sur la capacité de l’ORM pour obtenir les collections d’objets de façon simple, tout en exploitant sa capacité à gérer le lazy-loading

la conclusion fût : ok pour créer des collections de sous-ensembles dans le mapping, étonnant ;-)

Mapping

Alors comment faire pour arriver à ce que nous souhaitons faire ?

Le fichier de mapping .hbm.xml pour obtenir ces collections (parties bag AbonnementsExternes et AbonnementsInternes), on mettra bien évidemment ces collections en lazyloading afin de les charger que lorsqu’elles seront utilisées (ie : appel à la propriété) :

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true" >
  3. <class name="NewslettersConnector.Model.Newsletters, NewslettersConnector" table="NEWSLETTERS">
  4. <id name="Id" column="ID" type="System.Int16" access="property">
  5. <generator class="identity"></generator>
  6. </id>
  7. <property name="ProprietaireId" column="nl_propid" not-null="true" type="System.Int64" access="property"/>
  8. <property name="Email" column="nl_email" not-null="true" type="System.String" access="property"/>
  9. <property name="Libelle" column="nl_libelle" not-null="false" type="System.String" access="property"/>
  10. <property name="Description" column="nl_description" not-null="false" type="StringClob" access="property"/>
  11. <property name="DateCreation" column="nl_datecreation" not-null="true" type="System.DateTime" access="property"/>
  12. <bag name="Abonnements" fetch="select" access="property" lazy="true" inverse="true">
  13. <key column="nl_id"/>
  14. <one-to-many class="NewslettersConnector.Model.NewslettersAbonnements, NewslettersConnector" />
  15. </bag>
  16. <bag name="AbonnementsExternes" fetch="select" access="property" lazy="true" inverse="true"
  17. where="abo_typeabonne=1 and ind_id is not null">
  18.  
  19.  
  20.  
  21.  
  22. <key column="nl_id"/>
  23. <one-to-many class="NewslettersConnector.Model.NewslettersAbonnements, NewslettersConnector" />
  24. </bag>
  25. <bag name="AbonnementsInternes" fetch="select" access="property" lazy="true" inverse="true"
  26. where="abo_typeabonne=2 and ind_id is not null">
  27. <key column="nl_id"/>
  28. <one-to-many class="NewslettersConnector.Model.NewslettersAbonnements, NewslettersConnector" />
  29. </bag>
  30. </class>
  31. </hibernate-mapping>

La classe sérialisée pour le mapping :

  1. namespace NewslettersConnector.Model
  2. {
  3. [Serializable]
  4. public class Newsletters
  5. {
  6. private Int16 _id;
  7.  
  8. public virtual Int16 Id
  9. {
  10. get { return _id; }
  11. set { _id = value; }
  12. }
  13.  
  14. private long _proprioid;
  15.  
  16. public virtual long ProprietaireId
  17. {
  18. get { return _proprioid; }
  19. set { _proprioid = value; }
  20. }
  21.  
  22. private string _email;
  23. public virtual string Email
  24. {
  25. get { return _email; }
  26. set { _email = value; }
  27. }
  28.  
  29. private string _libelle;
  30.  
  31. public virtual string Libelle
  32. {
  33. get { return _libelle; }
  34. set { _libelle = value; }
  35. }
  36. private string _description;
  37.  
  38. public virtual string Description
  39. {
  40. get { return _description; }
  41. set { _description = value; }
  42. }
  43. private DateTime _datecreation;
  44.  
  45. public virtual DateTime DateCreation
  46. {
  47. get { return _datecreation; }
  48. set { _datecreation = value; }
  49. }
  50.  
  51. private IList<NewslettersAbonnements> _abonnements;
  52. /// <summary>
  53. /// Abonnements (tout type) - sérialisé par NHibernate, voir mapping Newsletters.hbm.xml
  54. /// </summary>
  55. [XmlIgnore]
  56. public virtual IList<NewslettersAbonnements> Abonnements
  57. {
  58. get { return _abonnements; }
  59. set { _abonnements = value; }
  60. }
  61.  
  62. private IList<NewslettersAbonnements> _abonnementsexternes;
  63. /// <summary>
  64. /// AbonnementsExternes - sérialisé par NH, voir mapping Newsletters.hbm.xml
  65. /// </summary>
  66. [XmlIgnore]
  67. public virtual IList<NewslettersAbonnements> AbonnementsExternes
  68. {
  69. get { return _abonnementsexternes; }
  70. set { _abonnementsexternes = value; }
  71. }
  72.  
  73. private IList<NewslettersAbonnements> _abonnementsinternes;
  74. /// <summary>
  75. /// AbonnementsInternes - sérialisé par NH, voir mapping Newsletters.hbm.xml
  76. /// </summary>
  77. [XmlIgnore]
  78. public virtual IList<NewslettersAbonnements> AbonnementsInternes
  79. {
  80. get { return _abonnementsinternes; }
  81. set { _abonnementsinternes= value; }
  82. }
  83. }
  84. }

L’interface du contrôleur qui sera implémentée :

  1. namespace NewslettersConnector
  2. {
  3. public interface INewslettersManager
  4. {
  5. Newsletters getNewsletter(int nlid);
  6. Newsletters getNewsletter(string email);
  7. void saveAbonnement(NewslettersAbonnements abo);
  8. void deleteAbonnement(NewslettersAbonnements abo);
  9. NewslettersAbonnements getAbonnement(int nlid, long indId);
  10. }
  11. }

Remarques

Dans la classe Newsletters, les propriétés sont en virtual, cela permet de profiter du lazy-loading le cas échéant. NHibernate pourra ainsi créer des objets internes qui hériteront de nos objets métiers, et dès lors, déclencher le chargement des collections lors de leur accès.

Une question d’un membre de l’équipe : vaut-il mieux utiliser les fichiers hbm.xml (et donc décrire le mapping sous forme XML) ou utiliser des attributs (comme le ferait ActiveRecord ou dsMap, notre ancien ORM) ?

Personnellement, autant que faire se peut, il est préférable de ne pas polluer la classe entité (ici Newsletters) par des attributs spécifiques à l’ORM, on se couperait d’une abstraction avec le framework de mapping. Si des attributs étaient utilisés, on se verrait obligés d’utiliser des classes DTO, transverses à toutes les couches, et de recopier les objets du mapping vers ces objets DTO, afin de garantir une abstraction, ce qui complique (recopie) et ajoute de la complexité (couche supplémentaire) au projet.

Sur ce, je vais me concentrer sur le match France-Italie, c’est mal parti…Ribery sorti sur blessure, carton rouge pour un autre joueur, pénalty…super…

Et n’oubliez pas : Firefox 3 record à battre.

Links #3 : Langages dynamiques Web – PHP, Rails

Posted by – June 16, 2008

  • Phalanger : PHP pour la plateforme .NET (Win & Mono) – et en plus, le site fonctionne sous DokuWiki sous…NET, ça donne des idées ;-) les benchs donnent envie.
  • RubyEE et les tests de performances sur différentes configurations. J’avais quitté Typo (v4.1 à l’époque) pour 3 raisons : gros soucis de consommation de ressources mémoires voire CPU, pas très user-friendly pour gérer les mises à jour du moteur sur le serveur (faisait plus appel à notre bonne étoile qu’une procédure bien carrée pour les migrations, prenait trop de temps), et des défauts reconnus de SEO. Je vais voir comment cela évolue, mais pourquoi pas ré-essayer, comme le mainteneur met pas mal d’énergie pour corriger ses principaux défauts.