dimanche 8 février 2009

Tutoriel developper des EJB3 remix





















Parti en expédition sur les terres du JEE, j'ai trouvé refuge dans le comté d'EJB3, comté certes séduisant en apparence, mais la simplicité faisant défaut a ses habitants j'ai décidé de faire un tutoriel largement inspiré de celui existant sur éclipse totale (http://www.eclipsetotale.com/articles/Introduction_EJB3_avec_Eclipse.html). Étant rebelle, je n'utiliserais pas Eclipse mais Netbeans, et j'utiliserais Glassfish en lieu et place de JBoss.
Pourquoi ces choix ? Tout d'abord pourquoi Glassfish ? Parce que ce serveur d'application JEE est léger il prend moins de 100 MO à vide contrairement à JBoss qui prend plus de 300 MO à vide, Parce que Glassfish est certifié JEE 5 et pas JBoss. Glassfish est certes peu connu (contrairement à JBoss) mais il est quand même conçu par Sun ce qui lui apporte une certaine crédibilité, il aurait d'ailleurs déposé Weblogic et Websphere dans le benchmark SPECjAppServer. Pourquoi Netbeans ? Là je dois avouer que c'est plus subjectif, les deux environnement se valent, Eclipse est plus réactif (grâce à SWT ?) et dispose de bien plus de plugins, il a de plus résolu ses problèmes de stabilité, Netbeans lui n'a pas beaucoup évolué et n'as pas vraiment comblé ses lacunes par rapport à Eclipse, mais son plugin pour Glassfish semble mieux fonctionner que celui d' Eclipse. J'ai eu l'occasion d'essayer JDevelopper d' Oracle, c'est de loin l' EDI le plus réussi esthétiquement parlant, mais la stabilité du logiciel est déplorable, elle est digne d'une version alpha. Bref, trêve de tergiversations, passons au tutoriel. La version de Netbeans utilisée ici est la 6.5, la version de Glassfish est la version 2.1 et la versions du JDK utilisé est la 6 update 11.

Créez un nouveau projet Java EE de type EJB Module dans Netbeans, ce projet est intitulé IntroEJB3, la version de JEE utilisé sera la 5 et le serveur utilisé sera bien évidement Glassfish V2.
Créez un package «com.et».Dans l'onglet « projects » faites un clic droit sur « Entreprise beans » cliquez sur « Session Bean » vous appellerez ce bean « PremierEJB3 » ce bean est de type « stateless » son interface est de type « remote » (décochez la case « Local »), ce bean sera dans le package «com.et». Deux fichier sont créés : « PremierEJB3Remote.java» et « PremierEJB3Bean.java», le premier correspond a l'interface « remote » du bean le second correspond à la classe d'implémentation du bean. Ouvrez le fichier « PremierEJB3Bean.java » faites un clic droit à l'interieur, cliquez sur « insert code » puis sur « add business method », appelez cette méthode « ditBonjour » donnez lui le type String comme « return type », ajoutez lui un paramètre de type String nommé « aQui ». Le contenu de cette méthode sera le suivant « return "Bonjour " + aQui + " !!!"; » . Il ne vous aura pas échappé que le prototype de la méthode a été rajouté dans l'interface « PremierEJB3Remote ». Vous pouvez tester votre travail en lancant le serveur, pour ce faire faites un clic droit sur le projet « IntroEJB3 » et en cliquant sur « Run », après quelques secondes un message doit vous afficher que le serveur est bien lancé et que le projet IntroEJB3 a été chargé par Glassfish, en cas de soucis n'hésitez pas à augmenter le timeout (surtout si votre machine est peu véloce) du lancement du serveur.

Passons au client, créez un projet Java de type « Java Application », appelez le « ClientEJB3 », créez un package « com.et ». Créez dans ce package une classe « ClientPremierEJB3 » qui contient le code suivant :

package com.et;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class ClientPremierEJB3 {
public static void main(String[] args) {
try {
Hashtable hashtableEnvironment = new Hashtable();
hashtableEnvironment.put("java.naming.factory.initial",
"com.sun.enterprise.naming.SerialInitContextFactory");
hashtableEnvironment.put("java.naming.factory.url.pkgs",
"com.sun.enterprise.naming");
hashtableEnvironment.put("java.naming.factory.state",
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
hashtableEnvironment.put("org.omg.CORBA.ORBInitialHost",
"localhost");
hashtableEnvironment.put("org.omg.CORBA.ORBInitialPort",
"3700");
Context context = new InitialContext(hashtableEnvironment);
PremierEJB3Remote beanRemote = (PremierEJB3Remote)
context.lookup("com.et.PremierEJB3Remote");
System.out.println(beanRemote.ditBonjour("ClientPremierEJB3"));
} catch (NamingException e) {
e.printStackTrace();
}
}
}

Vous notez ici quelques différence avec le tutoriel d'eclipse totale, d'une part j'ai renseigné les paramètres JNDI dans une hashtable et non pas dans un fichier « jndi.properties » et d'autre par la chaine de caractère passé en paramètre à la méthode « lookup » de l'objet « context », n'est pas la même.
Je n'ai pas utilisé de fichier « jndi.properties » pour des raisons de simplicité. La chaine passé en paramètre à la méthode « lookup » est différente parce que celle présente sur le tutoriel d'éclipse totale ne permet pas de retrouver le bean « PremierEJB3Bean ». De plus, quel est l'intérêt de créer une interface si le client cherche un bean à partir du nom de sa classe d'implémentation ? Si l'on utilise l'interface « remote » du bean « PremierEJB3Bean » dans la méthode « lookup » quel est l'intérêt de rajouter « /remote » à la fin de la chaine de caractère ? Afin de vous évitez quelques déconvenue je vous recommande l'ajout des jar suivantes : « appserv-rt.jar » et « javaee.jar ». La jar « appserv-rt.jar » vous évitera l'exception « classnotfoundexception » en effet vous avez besoin de la classe « com.sun.enterprise.naming.SerialInitContextFactory » qui se trouve justement dans ce jar. Il en est de même pour la jar « javaee.jar » qui vous permettra d'éviter le « classnotfoundexception » avec la classe « javax.security.jacc.PolicyException », ces jar doivent bien évidement êtres accessible lors de l'exécution du programme. N'oubliez pas de référencez le projet « IntroEJB3 » dans les librairies de « ClientEJB3 » pour la compilation et l'exécution du programme.

Utilisez la chaine "PremierEJB3Bean/remote" en paramètre de la méthode « lookup » et vous obtiendrez une magnifique « javax.naming.NameNotFoundException ». Si vous décidez d'utiliser la chaine "PremierEJB3Remote/remote"  en paramètre a cette même méthode vous obtiendrez une autre « javax.naming.NameNotFoundException ». En utilisant "com.et.PremierEJB3Remote/remote" vous obtiendrez une curieuse « javax.naming.NameAlreadyBoundException: com.et.PremierEJB3Remote », curieuse dans la mesure où nous n'essayons pas de refaire un binding, nous aurions utilisé la fonction «bind » pour cela. La chaine "com.et.PremierEJB3Remote", elle permettra à votre client de s'exécuter convenablement. Cela me semble logique dans la mesure ou en utilisant l'interface de type « remote » il est évident que l'on souhaite accéder à un bean distant, le suffixe « /remote » deviens donc inutile. Préfixer le nom de l'interface par son package me parait là aussi logique.

dimanche 25 janvier 2009

Politique

Des fois je me demande pourquoi, je me lève tôt cinq matins sur sept pour aller travailler 35 heures par semaines. Je me dis que j’aurais mieux fait de faire de la politique, j’ai une grande gueule, de la répartie, je sais baratiner, mes qualités oratoires sont reconnues. J’aurais du intégrer un grand parti politique, l’UMP ou le PS, les autres n’étant pas assez fortunés à mon gout, j’aurais fait partie de l’élite, de ces gens qui n’en ont rien à branler de la hausse du prix de l’essence, j’aurais fait partie de ces gens qui ont la chance de pouvoir faire leurs courses sans compter, d’avoir une carrière tracée, de pouvoir me préparer une retraite confortable. Peu importe mes opinions, peu importe mes idées, l’important ce n’est pas que je sois en accord avec mon parti politique, mais que je donne l’impression de l’être. L’important n’est pas de savoir de quoi l’on parle mais de donner l’impression que l’on sait de quoi l’on parle. Tout n’est qu’apparence.

Une base de données orientée objet : Db4o

Entre deux morceaux de death metal scandinaves votre dévoué serviteur se plaît à expérimenter de nouveaux outils informatiques. C’est ainsi que j’ai testé une base de données orienté objets. Pourquoi une base de données orientée objet ? Cela ne vous aura pas échappé que dans beaucoup de logiciels nécessitant la présence d’une base de données (afin de persister des données) des ORMs (object-relational mapping) sont utilisés (Hibernate, TopLink, Ibatis pour ne citer qu’eux). En utilisant des ORM on est confrontés (entre autres) a deux problèmes, soit des problèmes de performances (qui sont les prix à payer pour se détacher du SQL afin gagner en productivité), soit des problèmes de productivité (qui sont les prix à payer pour avoir un niveau de performance acceptable lorsque l’on manipule de gros volume de données). Alors certes, dans la réalité la situation n’est pas aussi binaire que mes propos le laisseraient supposer, en effet il est possible d’optimiser un ORM lent et d’utiliser des outils qui permettent de gagner en productivité lorsque l’on utilise des ORMs ne facilitant pas à la base la productivité. Dans le premier cas, il faut connaître sur le bout des doigts l’ORM ce qui en soit n’est pas trivial, cela demande aussi une bonne connaissance de l’optimisation de l’utilisation des bases de données (schémas, requêtes etc.). Dans le cas des ORM ne facilitant pas la productivité, les outils dont j’ai parlé plus tôt ne sont pas toujours disponibles, et lorsqu’ils permettent d’avoir un niveau de productivité correcte, c’est parfois au dépend de l’architecture du logiciel. Le compromis entre performances et productivité est possible, mais difficile à obtenir. Et si le problème était (entre autre) le manque d’adéquation entre le modèle objet et le modèle relationnel ? Et si un bon modèle objet était rarement transposable en un bon modèle relationnel et inversement ? Et si la solution était que la base de données nous libère du SQL ? Et si la base de données était capable de persister des objets sans avoir à définir de mapping que ce soit par des annotations ou des fichiers XML ? Ces interrogations pourront vous paraitre naïve voir utopistes, mais sachez qu’il existe des bases de données orientées objets qui sont capable de persister des objets Java .NET etc. Et ce sans la moindre information de mapping.

J’ai eu l’occasion d’essayer Db4o une base de données orientées objet libre, la facilité d’utilisation de cet outil est assez troublante. En l’utilisant on se demande vraiment pourquoi autant de gens s’embêtent avec des ORMs. Toutefois la prudence est de mise, si je pense que cette solution est adapté pour des petits projets, je n’ai pas pu faire de tests de performances avec cette base de données, je n’ai pas non plus essayé de persister des objets complexes. Le gain en productivité avec cette base de données est plus important selon moi qu’avec un ORM comme hibernate. Je trouve que ce type de base gagnerait à être connu dans la mesure où elles tentent de répondre à la demande actuelle, à savoir persister facilement et si possible rapidement des objets. J’ai décelé quelques points noirs, tout d’abord la documentation, le « Formula One Tutorial » bien qu’amusant à faire il est un peu négligé, d’une part il propose d’utiliser des méthodes dépréciés (problème de mise à jour de la documentation ?) d’autre part le « full source » est incomplet. Autre point noir, la licence, cette sympathique base de données est sous licence GPL, donc tout logiciel utilisant cette base de données bascule sous licence GPL…. Néanmoins ce type de base données trace une nouvelle voie : Plutôt que faire rentrer un model objet dans une base de données relationnelles ou un modèle relationnel dans un modèle objet à l’aide d’ORMs, autant utiliser une base de données capable de comprendre nativement un model objet.

Quelques bouts de codse pour montrer ce que l’on peut faire avec :

MyClass.java :

package com.db4o.f1.chapter1;

import java.io.File;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;

public class MyClass {
public static void main(String[] args) {
new File("c:/database").delete();
accessDb4o();
new File("c:/database").delete();
ObjectContainer db = Db4o.openFile("c:/database");
try {
storeFirstPilot(db);
storeSecondPilot(db);
retrieveAllPilots(db);
retrievePilotByName(db);
retrievePilotByExactPoints(db);
updatePilot(db);
deleteFirstPilotByName(db);
deleteSecondPilotByName(db);
} finally {
db.close();
}
}

public static void accessDb4o() {
ObjectContainer db = Db4o.openFile("c:/database");
try {
// do something with db4o
} finally {
db.close();
}
}

public static void storeFirstPilot(ObjectContainer db) {
Pilot pilot1 = new Pilot("Michael Schumacher", 100);
db.set(pilot1);
System.out.println("Stored " + pilot1);
}

public static void storeSecondPilot(ObjectContainer db) {
Pilot pilot2 = new Pilot("Rubens Barrichello", 99);
db.set(pilot2);
System.out.println("Stored " + pilot2);
}

public static void retrieveAllPilotQBE(ObjectContainer db) {
Pilot proto = new Pilot(null, 0);
ObjectSet result = db.get(proto);
listResult(result);
}

public static void listResult(ObjectSet result) {
System.out.println(result.size());
while (result.hasNext()) {
System.out.println(result.next());
}
}

public static void retrieveAllPilots(ObjectContainer db) {
ObjectSet result = db.get(Pilot.class);
listResult(result);
}

public static void retrievePilotByName(ObjectContainer db) {
Pilot proto = new Pilot("Michael Schumacher", 0);
ObjectSet result = db.get(proto);
listResult(result);
}

public static void retrievePilotByExactPoints(ObjectContainer db) {
Pilot proto = new Pilot(null, 100);
ObjectSet result = db.get(proto);
listResult(result);
}

public static void updatePilot(ObjectContainer db) {
ObjectSet result = db.get(new Pilot("Michael Schumacher", 0));
Pilot found = (Pilot) result.next();
found.addPoints(11);
db.set(found);
System.out.println("Added 11 points for " + found);
retrieveAllPilots(db);
}

public static void deleteFirstPilotByName(ObjectContainer db) {
ObjectSet result = db.get(new Pilot("Michael Schumacher", 0));
Pilot found = (Pilot) result.next();
db.delete(found);
System.out.println("Deleted " + found);
retrieveAllPilots(db);
}

public static void deleteSecondPilotByName(ObjectContainer db) {
ObjectSet result = db.get(new Pilot("Rubens Barrichello", 0));
Pilot found = (Pilot) result.next();
db.delete(found);
System.out.println("Deleted " + found);
retrieveAllPilots(db);
}
}



Pilot.java :

package com.db4o.f1.chapter1;

public class Pilot {
private String name;
private int points;

public Pilot(String name,int points) {
this.name=name;
this.points=points;
}

public int getPoints() {
return points;
}

public void addPoints(int points) {
this.points+=points;
}

public String getName() {
return name;
}

public String toString() {
return name+"/"+points;
}
}