Sunday, 17 September 2006

Astuce DWR -- encapsulez getSession()

DWR

Un des avantages de DWR, c'est que côté Java, son utilisation est invisible. Invisible mis à part l'emploi des méthodes statiques d'une classe glorieuse, uk.ltd.getahead.dwr.WebContextFactory, qui permet aux Listeners DWR de communiquer avec la session et ServletContext de l'application web. Sans cette classe, la gestion de l'état de l'application serait impossible (ou très difficile en tout cas). Il en resulte que nous retrouvions WebContextFactory.get().getSession() dans pratiquement toutes les méthodes de toutes les classes Listener (comme nous appelions les Creator déclarés dans dwr.xml).

L'expérience a pourtant montré que d'appeler directement WebContextFactory.get().getSession() dans les Listener n'est pas une bonne pratique. Du moins, dans notre projet on a découvert que ça nous enlevait de la flexibilité : en l'occurance, on voulait effectuer un contrôle supplementaire sur la session avant d'y accéder (genre AOP), pour gérer plus proprement l'expiration des sessions, par exemple.

Au lieu donc de vérifier dans chaque methode de chaque Listener qui utilise la session, que la session n'est pas invalidée (!!), on a centralisé la récuperation de la session dans un seul endroit, AJAXListener.getSession() (AJAXListener est la classe mère des listeners dans notre application, réunissant les fonctions d'utilité globale). Là on peut mettre le code pour gérer les sessions expirées (dans notre cas, en levant une fille de RuntimeException maison pour laquelle on a créé une gestion particulière avec DWREngine.setErrorHandler()).

Cette encapsulation a en plus le bénéfice que le code Java ne fait référence à dwr.jar que dans une seule méthode, au lieu de plusieurs douzaines, ce qui rend l'application moins dépendante de la couche technique sur laquelle elle répose.

Posted by jon at 10:21 PM in Java 
 

Sunday, 1 July 2007

Comment adapter une application Java à Mac OS X

L'avantage de Java, c'est qu'on peut écrire une application sous (par exemple) Windows, et elle marchera sur Linux et OS X sans qu'on a d'efforts particulier à faire. Lorsqu'on double-clique sur le .jar, il s'exécutera. Pourtant le cas d'OS X est un peu particulier, car même si l'approche simple marche, il manque un peu d'élégance, parce que OS X a quelques propriétés supplémentaires qui doivent être pris en compte pour que notre application a l'air vraiment natif. Heureusement, seulement quelques petites modifications sont nécessaires pour créer une application OS X avec notre application Java, et le résultat est très satisfaisant.

Pour suivre ce tutorial, vous aurez besoin de :

  1. Une application swing compilé dans un ou plusieurs .jar
  2. Un ordinateur Apple avec les outils de développement installés

Première étape—Lorsqu'on exécute une application Java sous OS X, le comportement par défaut est d'afficher le nom de la classe comme nom de l'application. Ceci est gênant, parce que en OS X ce nom est affiché en haut de l'écran. Quand j'ai écrit mon appli "Dépenses", je voulais que le mot "Dépenses" s'affiche en haut, et non pas fr.craven.depenses.Main! Heureusement il suffit de saisir une propriété pour spécifier le nom qu'on veut, en argument à la JVM ou dans sa méthode main avec une ligne comme suit: System.setProperty("com.apple.mrj.application.apple.menu.about.name","Dépenses"); (Comme les JVM non-Apple ignoreront cette propriété, ça ne fait pas de mal de le saisir systématiquement dans vos applications Swing.)

Deuxième étape—Maintenant notre application a déjà meilleur mine, mais il utilise toujours l'icône par défaut, au lieu d'un icône particulier à notre application, et cet icône par défaut laisse à désirer: Alors pour donner un autre image, il faut créer un fichier .icns. Heureusement, l'opération est aussi simple que de glisser notre image (.png ou autre) dans Icon Composer, une application livrée avec les outils développeur, et qui créera le .icns pour nous:

Troisième (et dernière) étape—Il s'agit de prendre notre fichier .icns et nos .jar et les mettre dans une application OS X. Encore une fois, l'opération est très simple grâce aux outils fournis avec OS X. Il faut dire aussi qu'une application OS X n'est en réalité qu'un répertoire ayant une structure précise et des méta-données dont OS X se sert pour mieux afficher notre application.

Donc, tout ce qu'il faut faire c'est d'ouvrir Jar Bundler, qui se trouve dans /Developper/Applications/Java Tools/. L'interface n'est pas trop compliquée :

Il suffit de glisser son icône sur l'image, et de choisir son .jar en cliquant sur le bouton. S'il faut spécifier d'autres jar ou options, les autres onglets laissent faire tout ce qu'il faut. Et voilà, on a maintenant une application OS X que les utilisateurs peuvent déplacer et installer comme les autres, avec son propre icône et son nom qui s'affichent correctement!

Posted by jon at 3:31 PM in Java 
 

Monday, 19 March 2007

Comment créer un composant JSF (deuxième partie)

Dans le premier article de cette série, j'ai décrit les étapes nécessaires pour créer la première classe qu'il faut écrire pour faire son propre composant JSF, la classe Tag. Maintenant on va continuer avec le vrai cœur d'un composant JSF, la classe UI. Je rappelle que nous sommes en train de créer un composant exemple simple qui s'appelle Tableau, et on peut visualiser le produit final ici.

Pour la classe UI donc, on a le choix entre trois classes mères possibles, selon le type de composant qu'on est en train de créer :

  • Est-ce que le composant se comporte comme un bouton ou un lien? Utiliser UICommand.
  • Est-ce que le composant affiche des quelque chose qui n'est pas modifiable? Utiliser UIOutput.
  • Est-ce que le composant saisit un valeur de l'utilisateur? Utiliser UIInput.

Notre exemple, un tableau qui se lie à un List<Object[]> pour son contenu, étend donc UIOutput. Ensuite on va coder la méthode qui produit l'html qui sortira sur la page :

public void encodeBegin(FacesContext context) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String id = getClientId(context);
    List<Object[]> rangees = (List<Object[]>)
        getAttributes().get("contenu");
    String style = (String) getAttributes().get("styleClass");
    writer.startElement("table", this);
    writer.writeAttribute("class", style, null);
    int rowCtr = 0;
    for (Object[] cellules : rangees) {
        int cellCtr = 0;
        writer.startElement("tr", this);
        writer.writeAttribute("class", style, null);
        for (Object cellule : cellules) {
            writer.startElement("td", this);
            writer.writeAttribute("class", style, null);
            writer.writeAttribute("name", 
                id + rowCtr + "@" + cellCtr, 
                null);
            writer.writeText(cellule, null);
            writer.endElement("td");
            cellCtr++;
        }
        writer.endElement("tr");
        rowCtr++;
    }
    writer.endElement("table");
    return;
}

La première ligne procure un ResponseWriter, qui simplifie la génération des balises html, et la deuxième donne un identifiant client, qu'on peut utiliser dans les attributs id ou name de nos balises html. Ce n'est pas très important pour notre tableau exemple actuel, mais si plus tard on veut l'aggrandir (en rendant cliquable les cellules, par exemple) on en aurait besoin.

Et voilà, nous avons créé un composant JSF ! Il ne fait pas beaucoup, c'est vrai, et il faudra beaucoup de travail pour le rendre aussi utile que le tableau proposé par Java Studio Creator, mais j'espère que les principes sont tous apparants par ce petit exemple. En plus, si vous voulez reprendre un composant existant et en modifier ou ajouter des fonctionnalité, vous avez maintenant une vue globale sur ce qu'il ne faut pas casser!

L'installation n'est pas trop complexe, mais il faut vérifier que vous n'oubliez pas une étape.

  1. Mettre TableauTag.class et UITableau.class dans le classpath de l'application.
  2. Mettre votre .tld dans /WEB-INF ou le classpath
  3. Enscrire votre .tld dans web.xml
  4. Enscrire votre composant dans faces-config.xml

Toutes ces étapes sauf la dernière sont déjà bien connues par les dévelloppeurs Java EE. L'inscription dans faces-config.xml est très simple, il suffit d'ajouter les lignes suivantes :

  <component>
      <component-type>fr.craven.test.jsf.Tableau</component-type>      
      <component-class>fr.craven.test.jsf.UITableau</component-class>
  </component>

(Veillez à ce que component-type s'accorde avec la valeur que votre classe Tag retourne dans sa méthode getComponentType().)

Pour voir notre beau tableau en action si vous ne l'avez pas déjà fait, cliquer ici ; la code source du jsp exemple est visible ici. Il reste un dernier morceau au puzzle : l'intégration dans l'EDI pour pouvoir travailler visuellement avec notre composant. Cela attendra le troisième volet de cette série, où je montrerai comment ajouter notre composant Tableau dans la palette NetBeans.

Comme toujours, laissez un commentaire si vous avez des questions, ou si quelque chose n'est pas clair!

Posted by jon at 12:01 AM in Java 
 

Wednesday, 14 March 2007

Comment créer un composant JSF (première partie)

Ça ne prend pas beaucoup de temps pour être séduit par JSF, et sa façon de simplifier le développement grâce aux composants (au lieu de coder du html, JavaScript, et css à la main, et réinventer la roue à chaque fois—car même en créant ses propres taglibs, on ne peut pas toujours encapsuler facilement les dépendances html et client-serveur de ceux-ci).

Mais pour un grand projet, avec des besoins plus poussées, on peut vite s'interroger sur la facilité de créer ses propres composants JSF. Car même si on peut puiser dans les ressources très larges des composants proposés par Sun, Oracle ADF Faces, JBoss RichFaces, Apache Tobago, Woodstock, et tant d'autres, il y aura toujours le besoin de créer des composants vraiment propres à son site ou entreprise. Alors, dans cette série d'articles, je donnerai un survol de la création d'un composant JSF, pour en expliquer ce que cela implique. Il faut souligner que les développeurs qui utiliseront vos composants ignoreront tous ces détails ; c'est là toute la beauté de JSF.

Le composant qu'on va développer ici est très simple, car on ne veut pas se perdre dans les détails. D'ailleurs j'assume que vous possédez déjà les connaissances JSF et JSP, notamment la création des taglibs il existe déjà beaucoup de bons tutoriaux sur ces sujets ailleurs, alors ce n'est pas la peine que je les rexplique ici. Il s'agira pour notre composant d'un simple tableau, dont on peut spécifier la classe CSS et le contenu, un value binding vers un List<Object[]>. Dans le JSP on pourra donc écrire
<craven:tableau contenu="#{SessionBean1.contenu}" styleClass="cool"/>, et à l'écran on aura  (avec les valeurs de notre List en session) :

Vous pouvez visualiser le produit final en direct ici.

Au minimum, il va falloir écrire deux classes Java pour faire un composant. Par convention, le nom de la premiére classe finira avec Tag, par exemple TableauTag (car notre composant s'appelle "Tableau"). Celle-ci gère les attributs de la balise. La seconde commencera par UI pour "User Interface" (IHM), comme UITableau, et sert à gérer l'état du composant, rendre sa répresentation visuelle, et digérer la saisie. C'est donc cette seconde classe qui est la plus complexe, alors on commencera par la très simple classe Tag.

  • D'abord, il faut que la classe qui va définir votre composant étend javax.faces.webapp.UIComponentTag. Ainsi il supportera d'office les attributs standards binding, id, et rendered.
  • Ensuite, il faut ajouter les getters et setters pour les autres attributs du tag que vous voulez définir (du genre max, min, value, etc.).
  • En plus de ça, il faut définir les méthodes public String getComponentType() (qui donne un identifiant pour votre composant, comme, pour notre exemple, "fr.craven.test.jsf.Tableau"), et public String getRendererType() (qui donne un Renderer, s'il n'y en a pas on retourne null.)
  • Aussi, il faut réimplementer void setProperties(UIComponent uic), en commençant systematiquement avec super.setProperties(uic);. Ensuite, on ajoute dans cette méthode le code pour ajouter vos propres propriétés dans l'objet UI que vous allez créer. Il faut prendre en compte que les propriétés peuvent être des value bindings, la méthode privée setProperty() dans le fichier exemple est une façon facile d'encapsuler tout ça.
  • Dernier étape, il faut implementer public void release(), qui commence systematiquement par super.release();, et qui remettra l'état de l'objet à l'état originel (en mettant tous vos propriétés à null).

Pour en voir l'exemple complet, regarder TableauTag.java.

Créez un fichier .tld comme pour n'importe lequel taglib, et on a fini pour ce qui est la partie JSP de notre composant ! Pour la deuxième partie du boulot (la classe UI qui rendra tout ça sur la page) voir la prochaine article dans cette série. (N'hésitez pas à laisser vos questions ou commentaires ci-dessous pour tout ce qui n'est pas clair ; je m'efforce d'être bref dans ces tutoriaux, mais il faut quand même que ce soit clair.)

Posted by jon at 11:33 PM in Java 
 

Saturday, 9 February 2008

Comment générer des PDFs très longs avec JasperReports

La solution Open Source JasperReports est déjà bien connu et bien respecté dans l'informatique d'entreprise pour générer des reports, sous format .pdf, Excel, html, et autres. Sur un projet récent, on avait cependant un problème: nous nous retrouvions avec un pdf à générer qui faisait plus de 700 pages, et le pauvre serveur chargé de le produire plantait en OutOfMemoryError. Après débogage, on a vu que le coupable était JasperReports lui-même, qui plantait lors de la création du JasperPrint. Ma première réaction était donc d'éviter de créer un JasperPrint, et d'utiliser des méthodes qui permettent de créer un report directement avec des InputStream et OutputStream. Mais l'utilisation d'un PipedInputStream oblige déjà l'utilisation des Threads, ce qui n'est pas strictement permis dans une application J2EE (la gestion des threads étant le domaine du serveur). Mais même avec ces modifications, les problèmes des OutOfMemoryError persistaient. Heureusement la solution s'est ensuite présentée, et donc dans ce petit article, je vais expliquer comment on peut utiliser JasperReports pour créer des documents de n'importe laquelle taille, sans avoir des soucis de mémoire.

La solution qui a finalement marché était moins radicale que cette première idée. En fait on continue à utiliser un JasperPrint, alors les effets sur le reste notre code étaient minimes. Il suffisait en effet d'ajouter les lignes suivantes juste avant sa création:

JRSwapFile swapFile = new JRSwapFile("/tmp/", 0x400, 0x400);
JRAbstractLRUVirtualizer virtualizer = 
  new JRSwapFileVirtualizer(0x40, swapFile, true);
parametres.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
JasperPrint print = JasperFillManager.fillReport(
  jasper, /* notre fichier .jasper compilé */
  parametres, /* notre Map des paramètres */
  new JRBeanCollectionDataSource(donnees)); /* notre Collection des données */

(La dernière ligne est inchangée de notre classe d'origine, mais je l'inclus pour vous donner le contexte.) Que font ces trois nouvelles lignes? En fait, notre ancienne solution plantait parce que le JasperPrint qu'on gardait en mémoire était trop grand. Les Virtualizer de JasperReports permettent de déclarer des fichiers tampons, qui sont utilisés par Jasper pour écrire une partie de ses données sur disque. En ajoutant un virtualizer dans le Map des paramètres, Jasper va automatiquement se servir de cette fonctionnalité (dans notre cas, en écrivant un fichier tampon dans le dossier temporaire /tmp/; il existe aussi d'autres types de Virtualizer).

Dans notre projet, le simple ajout de ces trois lignes supplémentaires a suffit pour qu'on sorte nos .pdf de plus de 700 pages sans problème. Les vrais experts de JasperReports remarqueront quand même qu'on utilise un JRBeanCollectionDataSource, ce qui implique que nous avons une Collection en mémoire avec nos données. On peut imaginer que, si notre rapport était vraiment énorme (disons 7000 pages), on n'aurait pas assez de mémoire pour stocker toutes ces données en mémoire. En effet, si le volume des données est trop important, il faut implémenter un JRDataSource qui permet de lire les données un à un, au lieu de tout prendre en mémoire à l'avance. (JRResultSetDataSource répond déjà à ce critère.)

Si on fait attention sur ces deux points, d’utiliser un JRDataSource extensible, et d’inclure un REPORT_VIRTUALIZER dans les paramètres fournis au JasperFillManager, on pourra créer des JasperReports extrêmement volumineux sans avoir à se soucier de l'utilisation de mémoire.

Posted by jon at 7:37 AM in Java 
 

Tuesday, 13 February 2007

Comment rediriger System.out et System.err vers un fichier

Récémment je déboguais une application qui tournait correctement sur ma machine de développement, mais sur la machine client avait des problèmes. Comme il s'agissait d'une simple application Swing il n'y avait pas de log; visiblement il y avait des exceptions inattendues qui se declenchaient mais comme le stack trace ne s'affichait que sur System.err (que je ne pouvais pas voir), je n'avais aucune idée de ce qui se passait et j'ai dû me déplacer.

J'ai donc bricolé un petit utilitaire pour rediriger très simplement System.out et System.err vers un fichier. Il ne s'agit pas d'une solution pour remplacer java.util.logging ou Log4j, mais dans certains cas il peut être très utile, alors je le partage avec le monde. Tout ce qu'il faut faire c'est d'inclure les lignes suivantes dans votre classe d'entrée (là où main sera appelé):


  static {
    StdRedirect.redirect("myLogFile.log");
  }
  

Par exemple. Désormais, le fichier myLogFile.log recevra tous les messages destinés à la console. *Inutile de vous préciser que dans un vrai programme, vous préciserez le chemin de façon portable : StdRedirect.redirect(System.getProperty("user.home") + File.separator + "myLogFile.log");. La code source est presqu'aussi simple ; vous pouvez le visualiser ici (GPL).

Avec ça, l'utilisateur aura donc un fichier à vous envoyer si jamais vous rencontrez un bogue qui ne devrait pas être là, et le déboguage de vos applications Swing sur Windows sera sans doute simplifié!

Posted by jon at 7:02 PM in Java 
 

Tuesday, 5 June 2007

EJB 3.0 JPA, JDO, Hibernate? Quelques opinions

La persistance est un des sujets les plus discutés dans le monde de Java EE, et pour cause. En entreprise, les données sont la possession le plus précieuse de la société, et les logiciels informatiques qui gèrent et touchent à ces données sont donc très sensibles. Pourtant, ce n'est pas toujours facile de s'y retrouver en Java, surtout suite à la propagation des solutions de persistance qui ont été publiés depuis l'échec des Entity Beans dans l'ancienne version d'EJB. Je fais ici un petit survol de la situation actuelle des solutions pour la persistance en Java; ce n'est pas un traitement complet du sujet, mais j'espère au moins aider le lecteur qui s'y connaît un peu de se retrouver un peu mieux dans ce domaine.

D'abord, il faut absolument distinguer deux types de persistance: la persistance d'objet et la modélisation relationnelle des données. La persistance d'objet est le plus simple d'un point de vue d'un programmeur débutant. On a des objets, en on voudrait les garder pour les récupérer plus tard. Même si ce type de persistance est très simple, il ne faut pas oublier que dans certains cas, il est suffisant.

Cependant dans d'autre circonstances (notamment les circonstances rencontrées presque toujours en entreprise), la persistance d'objet ne suffit pas. Et ce notamment parce que les données ne sont pas la propriété d'un seul logiciel ou même d'un seul plateforme, mais elles appartiennent à toute la société. Lorsque quelque chose est saisie dans une application, les autres applications vont vouloir en tenir compte elles aussi.

Dans une telle situation, la persistance d'objet direct devient rapidement un bazar ingérable. Le modèle relationnel nous sort de cette impasse. Il fournit une façon mathématiquement formelle de décrire et modeler nos données. Les bases de données SQL telles DB2, Oracle, et SQL Server permettent de stocker des données de façon relationnelle. Elles fournissent en plus d'autres fonctionnalités importantes pour l'entreprise, notamment la gestion de l'intégrité transactionnelle et des back-ups. Le programmeur Java EE se doit de bien comprendre l'intérêt et le fonctionnement des bases de données relationnelles, et de la normalisation. C'est vraiment une connaissance indispensable dans ce métier.

Un problème intervient, pourtant, lorsqu'on veut utiliser une base de données relationnelle avec un programme Java, car en Java on travaille avec les objets discrets, et non pas les tuples et les relations. Le problème du mapping objet-relationnel ou ORM, décrit le processus par lequel on traduit nos structures de données entre ces deux mondes conceptuels différents.

Maintenant qu'on a définit les deux types de persistance, on peut regarder les solutions qui existent en Java.

I. Si la persistance d'objet nous suffit, ce qui est généralement le cas lorsqu'on veut juste sauvegarder les données pour son application, sans nécessairement devoir les communiquer avec d'autres systèmes, deux solutions sont possibles.

1. La façon la plus directe de faire cela en Java c'est en implémentant java.io.Serializable. Le désavantage c'est que les fichiers .ser sont vraiment particuliers à Java, et ne peuvent pas être exportés vers un autre système. Aussi, la persistance n'est pas centralisée. Enfin, on ne peut pas rechercher dans les objets stockés sans les charger tous préalablement, ce qui dans beaucoup de situations n'est simplement pas réaliste. Serializable n'est donc une solution que pour les applications avec les besoins de persistance les plus simples.

2. On peut opter pour JDO, un framework qui fait partie du Java standard (qui réside dans le package javax.jdo) et qui existe en plusieurs implémentations matures. (Si vous débutez dans le JDO, je conseille d'essayer l'implémentation gratuite, JPOX.) JDO travaille avec les objets en tant qu'objets (à la différence des solutions ORM ici-bas), mais peut tout de même se servir d'une base de données pour stocker ses informations. (D'autres formes de stockage, comme l'XML, sont aussi possibles.) Un système basé sur JDO peut commencer très simplement et devenir peu à peu très large et très complexe, ce qui est un fort avantage. Puissant et facile d'utilisation, JDO peut être la solution optimale dans beaucoup de cas. Pourtant, en prenant l'objet et non pas le modèle relationnel comme sa base de fonctionnement, avec JDO on sacrifie le sûreté que seul une base de données relationnelle normalisée peut fournir. (Ou bien, on fait un effort supplémentaire pour assurer que les données sont normalisées dans la base, mais JDO ne nous aide pas beaucoup dans cette démarche.)

II. Si une base de données relationnelle nous est nécessaire, il y a trois choix principaux qui semblent avoir un avenir.

1. On peut opter pour la JDBC. Avec cette solution, on prend en charge le mapping object-relationnel nous-mêmes. On a l'avantage de contrôler toutes nos requêtes, mais on est obligé de coder beaucoup plus aussi. Un avantage, en plus du contrôle, c'est qu'il suffit de connaître Java et SQL pour s'y mettre, plutôt que d'apprendre un nouveau framework. Mais en échange, on est obligé de coder beaucoup plus de code répétitif, juste pour pouvoir constituer nos objets. Souvent on découvre qu'on passe plus de temps à coder de la plomberie JDBC qu'à coder la partie métier de notre application!

2. On peut adopter le plus populaire des frameworks ORM, Hibernate. Avec Hibernate, on définit le mapping objet-relationnel dans des fichiers XML, et évite ainsi de dupliquer le travail en écrivant des requêtes à droite et à gauche (avec la possibilité d'erreurs que cela entraîne). Très populaire et open source, on retrouve facilement des conseilles et des outils pour travailler avec Hibernate sur internet. Pourtant, à la différence de JDO et JPA, Hibernate ne fait pas partie du Java standard, et JBoss est la seule société à le supporter commercialement.

3. On peut opter pour le nouveau standard, sorti avec le nouvel EJB 3.0, la JPA (Java Persistence API) (qui réside dans le package javax.persistence). JPA ressemble à Hibernate, mais au lieu de gérer un fichier XML pour chaque classe qu'on veut persister, on peut se servir des annotations directement sur la classe pour indiquer sa correspondance sur la base de données. Se servant ainsi de Java 5, en pratique JPA est beaucoup plus facile à gérer, et il a l'appui de tous les vendeurs du monde Java (Sun, Oracle, BEA, JBoss, Apache, etc.). Les désavantages principaux sont le fait qu'il faut utiliser la version Java 5 ou supérieur, et que JPA est plus jeune que les autres solutions mentionnés ici, qui ont tous eu plus de temps pour faire leurs preuves.

Voilà, c'est un survol des solutions principales de persistance. Evidemment le sujet est grand et je n'ai pas la place ou le temps d'expliquer chaque solution en profondeur. Mais souvent la première question qu'on a, c'est "par où commencer", alors j'espère que maintenant le lecteur pourra cerner lui-même si c'est une solution objet ou ORM qu'il faut, et ensuite laquelle correspond le mieux aux besoins présentes. Si le modèle relationnel n'a pas un intérêt central, JDO peut offrir le meilleur compromis entre puissance et simplicité. Mais dans le plupart des cas, le nouveau standard JPA sera le plus fréquemment le meilleur choix. Pourtant, si votre projet et dans Java 1.4, ou si vous avez déjà des programmeurs qui le connaissent sur le projet, vous opteriez plutôt pour Hibernate.

Et pour ceux qui préfèrent continuer à coder en JDBC à la main, pensez à migrer vers Java 6—cette version la plus récente de Java revoit et simplifie le JDBC pour le plus grand bonheur des programmeurs!

Posted by jon at 7:23 AM in Java 
 

Wednesday, 18 October 2006

Faire marcher l'envoi des e-mails avec Blojsom et JBoss—Première partie

JBoss

Récemment j'ai voulu configurer mon blog pour qu'on m'envoie un e-mail à chaque fois que quelqu'un y fait un commentaire. Cette fonctionnalité existe déjà dans Blojsom, mais sur mon déploiement il ne marchait pas correctement. Voici donc les étapes que j'ai suivi pour le faire marcher sur JBoss 4.0.4, au cas où quelqu'un d'autre serait bloqué avec le même problème.

D'abord il faut s'assurer que votre installation de JBoss est configurée correctement pour envoyer des e-mails. On peut configurer Blojsom à la main, mais ça sert à quoi d'avoir une serveur d'applications si on ne configure pas ses fonctionnalités pour que toutes vos applications puissent en bénéficier facilement?

Pour tester si l'envoi des e-mails marchait sur le serveur j'ai bricolé un petit .ear pour tester, avec Netbeans 5 Enterprise Pack ça peut se faire très rapidement, mais si vous me faites confiance vous pouvez utiliser le mien. Surtout ne laissez pas cet .ear déployé sur un serveur qui est ouvert à l'internet, sinon n'importe qui pourrait envoyé des e-mails à partir de votre serveur et vous serez vite blacklisté comme un spammeur!

JBoss configure le service mail par défaut avec les paramètres qui se trouvent dans $JBOSS_HOME/server/default/deploy/mail-service.xml (remplacer default par le nom de votre serveur s'il est différent), là vous mettez tous vos parametres de configuration, pareil que quand vous configurer un client mail (Thunderbird, Mail.app, etc.). Au lancement de JBoss, vous verrez dans le log si le service a pu demarré correctement, il sera mis à l'emplacement jndi java:/Mail.

C'est ici où ça peut vous confondre si vous ne le saviez pas. Toute adresse jndi qui commence par java:/ en JBoss est privée au serveur; vos applications ne peuvent pas le voir. Donc si vous essayer d'obtenir le service mail par java:/Mail, vous aurez une exception comme quoi c'est introuvable, alors que vous venez de vérifier que ça marchait! Dans vos applications, vous mettez l'adresse jndi que vous voulez, typiquement java:comp/env/mail/Mail (il faut que ça commence par java:comp/env/). A ce niveau-là, votre code est donc independent du serveur sur lequel il sera deployé—astucieux! C'est dans jboss-web.xml (jboss-jar.xml pour envoyer des e-mails à partir des EJB) que vous allez spécifier à quoi correspond l'adresse. En voici l'exemple:

<jboss-web>
    <resource-ref>
        <res-ref-name>mail/Mail</res-ref-name>
        <jndi-name>java:/Mail</jndi-name>
    </resource-ref>
</jboss-web>

(On omet le java:comp/env/ du "res-ref-name" car JBoss sait déjà que les adresses commencent avec ça.) Ce fichier sert donc de pont entre l'adresse indépendante de votre application d'un côté et l'adresse interne du serveur de l'autre. Si ça marche, nous savons que notre serveur J2EE est configuré correctement pour envoyer des e-mails. On est prêt maintenant à configurer Blojsom pour utiliser notre service e-mail ; j'expliquerai ça dans la seconde partie de cette article.

Posted by jon at 9:14 PM in Java 
 

Thursday, 19 October 2006

Faire marcher l'envoi des e-mails avec Blojsom et JBoss—Seconde partie

Blojsom

Dans la première partie de cet article, j'ai expliqué comment j'ai configuré mon serveur JBoss 4.0.4 pour pouvoir envoyer des e-mails depuis mes applications web et EJB. Dans cette seconde partie, j'explique comment configurer Blojsom 2.3 pour qu'il envoie automatiquement un message à chaque fois que quelqu'un laisse un commentaire.

D'abord, il faut activer le plug-in sendemail. Pour ce faire, dans le mode administration du blog, on va dans Plugin Settings -> Mappings et on l'ajoute dans "html plugin chain map" vers la fin. (On peut aussi modifier directement $BLOJSOM_HOME/WEB-INF/default/plugin.properties, où default est le nom de votre blog).

On pourrait croire que c'est tout ce qu'il faut faire, mais la configuration par défaut de Blojsom n'assume pas une adresse jndi par défaut comme le java:comp/env/mail/Mail de mon exemple précédent ; il n'assume même pas qu'on va le charger par jndi: on pourrait aussi spécifier directement dans le configuration Blojsom le nom du serveur SMTP et les autres paramètres (ce que je déconseille).

Il nous faut donc modifier notre web.xml. Trouvez l'init-param qui s'appelle smtp-server, et y mettre le valeur de l'adresse jndi complet (ici, java:comp/env/mail/Mail). Blojsom sait qu'il s'agit d'une adresse jndi et non pas un nom du serveur grâce au préfixe java:comp/env. Si votre web.xml contient des init-params smtp-server-username et/ou smtp-server-username il faut les enlever : c'est le service J2EE qu'on a configuré qui s'occupe de tout ça.

Une dernière étape : il faut éditer $BLOJSOM_HOME/WEB-INF/default/plugin.properties, où default est le nom de votre blog. Vérifier que blog-owner-email indique bien l'adresse électronique où vous souhaitez recevoir des alertes, et mettre la ligne suivante comme suit : blog-email-enabled=true.

Et voilà, si tout marche comme ça devrait vous recevrez maintenant des alertes par e-mail de chaque commentaire et trackback sur votre blog. C'est très pratique pour réperer quand quelqu'un fait référence à un article ancien, et ça évite aussi de laisser infiltrer les spams. Les e-mails émis par Blojsom ont même des liens cliquables dans le corps du message pour supprimer ou approuver les commentaires et trackbacks sur le coup. Bon amusement!

Posted by jon at 9:00 PM in Java 
 

Tuesday, 22 August 2006

My letter discussed on Java Posse 78!

I was happily surprised to hear mention and discussion of my recent e-mail to the Java Posse podcast read in episode 78. They did a pretty good job summarising what was really quite a rambling letter, and also echoed my frustration about Java EE 5 not running on WebSphere yet. Anyway this is a pretty geeky thing to be proud of but it made my morning all the same :-)
Posted by jon at 7:07 PM in Java 
 

Monday, 10 December 2007

NetBeans 6 est sorti

La nouvelle version de NetBeans, mon EDI préféré, est maintenant disponible. La nouvelle version comporte plein de nouvelles fonctionnalités et améliore beaucoup de choses par rapport à la version 5.5. Un des plus grandes nouveautés est l’inclusion des modes pour travailler avec le langage du moment Ruby et son framework Rails.

La seule chose qui m’embête encore chez NetBeans, c’est que la Moblity Pack, qui permet de développer pour des téléphones portables, n’est pas disponible sur Mac OS X, même avec un processeur Intel. J’espère que cela changera un jour car tout mon developpement personnel se fait sur mon iMac maintenant et je ne peux plus donc faire du developpement mobile du tout à la maison. Mais bon, c’est le cas pour Eclipse aussi alors ce n’est pas vraiment la faute de NetBeans.

Posted by jon at 6:22 PM in Java 
 

Thursday, 3 August 2006

One Java coder's view of the world

The last episode of the Java Posse included a call for listeners' opinions of the new EJB3 spec. I got a bit carried away, going on to include my opinions on pretty much everything Java-related, and so I have decided to spin that off into an entry here to share with the world.

  • EJB3 - LOVE it. Both for persistence and the new session beans. Changed my home server from Geronimo to JBoss just because I was tired of waiting for it to come out on Geronimo (and also because now that RedHat owns JBoss a lot of my former qualms about the company have gone away). I also use the Glassfish that comes with the Netbeans 5.5 beta for development. The new EJB3 book from O'Reilly is also the best O'Reilly book I've read, although obviously that's very subjective.

    But all is not well, because I think it may be 5 years before I get to use this (or JSF or Java 5 generally) at work. At any rate it's not a possibilty now. What are the top 3 J2EE environments in market share? I'm not positive but I think it's IBM, Bea, and Oracle. And IIRC none of them support EE 5 yet--and IBM seems downright glacial about bringing it to WebSphere. Add to that that companies in the region I work in are very slow to adopt new versions. For the project I'm doing now I have to use Struts 1.1 for J2EE 1.3 :-(

  • JSF - Creator is cool but I haven't learned enough of the plumbing yet. I think my next project will use Seam and I hope to get better at it. Clearly JSF is the way of the future.

  • DWR - Using this for my current project and it is gorgeous (especially next to Struts 1.1). It's in many ways the opposite of GWT--you can do everything in JavaScript, and behind the scenes it actually calls Java methods. (At least, that's one way to conceptualise it.) Anyway it makes it dead simple to get what you want done with almost no plumbing. I think eventually AJAX will be reduced to internals for JSF components, but in the mean time this is great. (Because of that prediction for the future, GWT and the other frameworks don't interest me so much.)

  • Spring - Used it on last project and on nearly all my personal projects. Very nice, but I'm rooting for EJB3 to become the more prevalent. It may not though, as EJBs have such a bad reputation.

  • Struts - Legacy code. Anyone who defends it is only defending the time they invested to learn it because they are unwilling to learn anything else.

  • NetBeans - At home I use the mobility pack on Linux (just for playing around) and the 5.5 enterprise beta on my mac. Really great for tools but the editor is just not as good as Eclipse. I typically use NetBeans as a crutch to learn new APIs or technologies well enough to code them by hand, and then move to Eclipse for the real coding. My hands are hardwired to emacs keybindings and Eclipse is a little more complete with its emacs compatibility mode as well.

  • .net - It may be a threat to Java SE but that's not what's interesting to me anyway (I am a Linux guy so I try to stick to the enterprise space so I can train at home with free tools). Microsoft forces you to either be dishonest or pay hundreds of dollars (keeping in mind that I don't even have a Windows XP license) to learn their platform, and neither option appeals to me. How can something that doesn't even run on most servers be considered a threat to Java EE?

  • certifications - I have SJCP and SCWCD, as well as 4 IBM certifications (2 in DB2, 1 in XML & Web Services, 1 for WebSphere AD). All were to strengthen my resume when I was looking for a job, and they did that. The sun ones were full of trick questions, but that forces you to really memorise the APIs in detail, and I actually did get a lot out of doing them. The IBM ones were much easier to pass quickly if you know what you're doing, which is more fair, but on the other hand I didn't get that much useful work knowledge out of doing them. In the future I'll probably look into something like TopCoder instead, although if an EJB3 cert comes out I'll probabaly have a go at it. Certifications are invaluable for people trying to break into the industry with a non-CS degree, but certainly not needed for people with years of professional experience. Even with a CS degree it's a good way to show you have the practical skills as well.

  • derby in the jdk - I wrote a (very insignificant) patch for derby, so if this means that I have contributed to the JDK in some sense then I'm happy :-) Derby also happens to have a very DB2-compatible SQL syntax which makes life simpler for me on a number of levels. It's a good tool that deserves the exposure it's getting.

Disclaimer: These are all just opinions, and a snapshot in time of them as well.

Posted by jon at 8:16 PM in Java 
 

Friday, 23 March 2007

Présentation de Guice

Mes excuses pour ceux qui attendent le troisième volet de mon tutorial sur comment créer un composant JSF, mais je viens de découvrir une nouvelle librairie tellement intéressante que je ne pouvais pas attendre pour en parler. Guice (prononcé comme juice, "jus" en anglais) est une nouvelle librairie de Google pour faciliter l'inversion de contrôle et l'injection des dépendances. Si vous connaissez le framework Spring ou l'annotation @javax.annotation.Resource, vous savez déjà combien ce design pattern peut rendre votre code plus souple, en le rendant plus indépendant des autres composants du programme. Je ne vais pas expliquer le design pattern ici car j'estime qu'il est déjà bien connu, et sinon les explications que vous pourrez trouver ailleurs sur la toile l'expliqueront mieux que je ne pourrais le faire ici.

Mais je pense que la plupart d'entre vous connaîtront déjà l'inversion de contrôle, l'ayant pratiqué avec le framework Spring. Alors la première question que vous allez demander c'est pourquoi apprendre à utiliser Guice si on connaît déjà Spring? Deux raisons principales sont à citer :

  1. La performance de l'inversion de contrôle avec Guice va typiquement entre 10 et 100 fois plus rapide que Spring. Ça veut dire qu'on peut envisager de l'utiliser dans les projets plus petits, les librairies utilitaires, etc., là où Spring pourrait être considéré comme trop lourd.
  2. Basé sur les annotations (introduites dans le langage Java dans sa version 5), Guice permet de définir les dépendances plus naturellement en langage Java uniquement, sans toucher à l'XML.

Bien sûr, Spring fait beaucoup plus que l'injection des dépendances, alors Guice ne peut pas et ne vise pas à remplacer ce framework. Mais, si votre besoin se limite à l'injection des dépendances, je crois qu'il y a des fortes raisons de préférer la nouvelle librairie ultraperformante de Google à Spring IOC. (D'ailleurs, Guice peut s'intégrer très bien avec le reste du framework Spring.)

Alors, comment ça marche? Prenons pour illustration un outil récent que j'ai codé qui écrit une table d'une base de données dans un fichier et peut le relire dans la table après. Il veut donc reposer sur des intérfaces afin de séparer le programme principal des différents types de fichier qu'on pourrait utiliser (.csv, .ixf, .xml, etc.). On a donc deux intérfaces, Table2File et File2Table. Dans notre programme principal on veut éviter de spécifier l'implémentation en dur dans le code, pour pouvoir le changer plus rapidement par la suite. Avec Guice, on code donc ceci dans notre classe principal :

public class DBCopy {

  private Table2File t2f;

  @Inject
  public void setTable2File(Table2File t2f) {
    this.t2f = t2f;
  }
...

Et ainsi Guice saura qu'il faudra injecter ce valeur. Ensuite, pour faire le lien entre l'intérface et son implémentation, on crée non pas un fichier XML, mais une classe Java qui implémente Module. En voici un exemple (pour une implémentation qui emploie des fichiers de tableur comme transport) :

public class SpreadsheetModule extends AbstractModule {
  protected void configure() {
    bind.(Table2File.class).to(Table2FileTableurImpl.class);
  }
}

Vous voyez, que c'est très court, facile à lire, et propre. Plus souple aussi, notre exemple ne le montre pas, mais on peut injecter plus que les simple setters, mais aussi des constructeurs, injecter des implémentations différentes selon les annotations sur le champ injecté, etc.—et ça reste facile à lire.

Alors, maintenant on a déclaré les champs qu'on veut injecter, et on a définit leurs implémentations dans une module. La dernière étape c'est de dire à Guice d'utiliser cette module pour injecter les dépendances. En voici le code :

...
public static void main(String[] args) {
  //imaginons que arg[0] c'est "fr.craven.dbcopy.impl.SpreadsheetModule"
  String moduleName = args[0]; 
  Module module = (Module) Class.forName(moduleName).newInstance();
  Injector injector = Guice.createInjector(module);
  DBCopy copieur = new DBCopy();
  injector.injectMembers(copieur);
  copieur.start();
}
...

Là je passe le nom du module en paramètre, dans un vrai programme on le mettra dans un fichier properties, un paramètre d'initialisation, ou quelque chose comme ça. On pourrait aussi le mettre en dur dans le code, bien sûr, mais si le but de l'inversion de contrôle c'est d'éviter de lier notre code à une implémentation précise, je ne vois guère l'intérêt.

Idéalement, on peut structurer notre programme pour que ce seul appel à injector.injectMembers() injectera toutes les dépendances de notre programme, car Guice sait cascader dans les classes membres et injecter à son tour dans ces classes là. Par exemple, mon implémentation de Table2File qui utilise les fichiers de tableur utilise elle aussi des interfaces, SpreadsheetWriter et SpreadsheetReader, qui sont implémentés par des versions csv, Excel, et (bientôt) ODF. Si je les ajoute dans mon module (bind(SpreadsheetWriter.class).to(CsvWriterImpl.class);), Guice saura les injecter aussi.

Voilà pour cette petite introduction à Guice, je vous conseille de visiter son site pour en savoir plus et de l'essayer. D'après les ingénieurs de Google, cette librairie est déjà très mûr et est utilisé en interne chez Google dans des très grands projets (dont Struts 2). Guice est disponible sous licence Apache. Comme toujours, si mes explications ne sont pas claires ou vous avez des questions, n'hésitez pas à laisser vos commentaires.

Posted by jon at 5:01 PM in Java 
 

Monday, 31 July 2006

Programmation AJAX avec DWR

Au travail j'approche à la phase final du développement d'une nouvelle application Web, franchement le plus beau travail que j'ai fait jusqu'ici. A mon arrivé chez le client les spécifications étaient pour un applet, car ils estimaient que les besoins étaient trop complexes pour le réaliser autrement. J'avais bricolé quelques prototypes pendant le weekend avant d'y arriver, et pour moi c'était faisable en AJAX aussi, avec un interface utilisateur beaucoup plus sympa de surcroît. Alors on est parti là-dessus, et le travail a été très réussit grâce à l'utilisation de DWR, qui masque toute la complexité et permet de faire appel aux méthodes Java directement depuis le JavaScript comme si c'était des fonctions. Vraiment sympa. Pour le reste l'environnement technique du site est un peu arriéré (ortho?), car on déploie sur WebLogic 8 (alors on n'a pas J2EE 1.4), et on est obligé (par le client) d'utiliser Struts 1.1. Ce n'est pas la fin du monde mais ça a fait que je passais énormement du temps avec la "plomberie" par rapport au temps dédié à l'appli même. Mais DWR c'est vraiment à retenir pour une solution légère, simple, et facile à utiliser.
Posted by jon at 10:08 AM in Java 
 

Wednesday, 20 June 2007

Quoi de neuf dans JDBC 4.0?

Le lancement récent de Java 6 a été moins dramatique que le lancement de Java 5. Alors que la version 5 de Java a introduit les génériques, les annotations, l'auto-boxing, et les enum, Java 6, lui, ne se vante que des améliorations des performances, quelques retouches des annotations standards, et surtout, JDBC 4.0.

Dans tout le monde Java, les librairies JDBC sont probablement (avec java.io.* peut-être), celles que nous avons utilisées le plus fidèlement depuis le plus longtemps, sans qu'elles aient changées tant que ça. Ce qui ne veut pas dire qu'elles étaient parfaites—quasiment chaque programmeur a son propre petit bibliothèque, écrit soi-même ou trouvé il y a des années dans un ancien boulot, ou sur la toile... Alors, maintenant que JDBC 4.0 est sorti, qu'est-ce qui a changé, et qu'est-ce que ça nous apporte par rapport à la version précédente?

1. Fini le Class.forName(...).newInstance() pour charger la classe de notre pilote. DriverManager.getConnection() est désormais suffisamment rusé pour trouver lui-même la classe pilote en parcourant notre classpath.

2. @Select et @Update, deux annotations avec beaucoup de potentiel. Ces annotations peuvent être combinées avec BaseQuery pour lancer les requêtes avec un minimum de code. En voici un petit exemple:

interface MesRequetes extends BaseQuery {

    @Select(sql="SELECT NOM, PRENOM FROM UTILISATEUR")
    public DataSet<Utilisateur> getUtilisateurs();

    @Update(sql="UPDATE UTILISATEUR SET NOM=?, PRENOM=? WHERE ID = ?")
    public boolean updateUtilisateur(String nom, String prenom, int id);
}
Pour en profiter de MesRequetes, on crée un instance comme suit: MesRequetes mesRequetes = connection.createQueryObject(MesRequetes.class). C'est donc très différent du JDBC traditionnel, mais je pense qu'une fois qu'on s'habitue à l'objet Query, ça va être sympa. (DataSet, vu dans le code exemple, est aussi une nouveauté JDBC 4.0. Il est essentiellement comme un ResultSet, mais il peut fonctionner aussi en mode déconnecté, et il implémente java.util.List, ce qui le rend plus sympa à parcourir.)

3. Et le reste: resultSet.getRowId() et preparedStatement.setRowId() nous permettent de travailler avec des ROWID des BDD telles DB2 et Oracle, ce qui est indispensable quand on a une table qui ne possède pas d'identifiant unique. Clob et Blob ont été améliorés, le plus intéressant c'est la méthode free(), qui permet de libérer les ressources de ces objets lourds. SQLException a maintenant beaucoup de classes filles plus descriptives, un peu comme IOException faisait depuis longtemps. Bref, il y a beaucoup de petites améliorations çà et là, notamment pour ceux qui utilisent les caractères chinois et japonais.

Alors quel bilan: qu'est-ce que ça change vraiment pour ceux d'entre nous qui codent encore du JDBC à la main? A mon avis ceux qui adoptent la nouvelle façon de travailler proposée par les QueryObject vont l'apprécier. Pour le reste, les changements ne sont pas énormes et concernent plutôt les détails. On ne va pas se plaindre pour ces améliorations, mais ils n'auront pas beaucoup d'impact non plus. Néanmoins ça vaut le peine d'essayer les nouvelles annotations @Select et @Update, à mon avis vous les trouverez meilleures que les boîtes à outils JDBC que vous utilisiez jusqu'à maintenant.

Posted by jon at 6:48 AM in Java 
 
« May »
SunMonTueWedThuFriSat
 123456
78910111213
14151617181920
21222324252627
28293031   
       
 
Older articles
Non enim id agimus ut exerceatur vox, sed ut exerceat.