Portage d’une application J2EE 1.4 de Weblogic 9.2 vers Jonas 4.10
Cet article reprend les tâches que j’ai dues mettre en oeuvre pour le portage d’une application J2EE, dans le cadre d’un projet. Il ne recherche en aucun cas l’exhaustivité.
Architecture de l’application
Le portage concetne une application Web, à base de JSP et de servlets, avec des EJBs session et MDB. La couche Web communique avec les EJBs, et les EJBs communiquent entre eux. De plus, les EJBs accèdent à une base de données relationnelle en JDBC, via une DataSource. L’application utilise le framework de traces log4j.
On notera que certaines bizareries ont été constatées dans l’application, comme l’accès aux EJB ou à la datasource via leurs noms JNDI, sans untilistion 'ejb-ref ou de resource-ref. Ceci nous oblige donc à respecter scrupuleusement les noms JNDI utilisés. Pour tous les EJBs sessions, les interfaces local et remote sont fournies, alors que seul l’accès local est nécessaire.
Déploiement
L’application est déployée sous forme d’un fichier ear, contenant un fichier war et un fichier jar d’EJBs, ainsi qu’un fichier jar de librairie externe à l’ear.
Les fichiers jar de librairies (log4j, driver JDBC,…) sont déposées dans le répertoire lib/ext/ de Jonas. Le fichier ear est déposé dans le répertoire apps/autoload/ de Jonas, ou dans apps/. Dans ce dernier cas, il faut déclarer le fichier ear dans la configuration conf/jonas.properties :
jonas.service.ear.descriptors myapp.ear
Couche Web
Le portage de la partie Web de l’application n’a demandé aucun effort ! Le fichier war a pu être déployé sans aucune modification.
Couche métier : EJBs
Pour les EJBs, la tâche principale consiste en la traduction du fichier spécifique weblogic-ejb-jar.xml en fichier jonas-ejb-jar.xml. Le format de ce fichier doit correspondre à une version récente de Jonas, afin de profiter de certaines évolutions indispensables, comme la spécification du nom JNDI pour l’accès local.
<jonas-ejb-jar
xmlns="http://www.objectweb.org/jonas/ns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.objectweb.org/jonas/ns
http://jonas.ow2.org/ns/jonas-ejb-jar_4_8.xsd">
</jonas-ejb-jar>
EJB Session
Pour chaque EJB session, un élément <jonas-session> doit être ajouté. Cet élément contient le nom JNDI remote et, depuis Jonas 4.8, le nom JNDI local, ainsi que le dimensionnement du pool d’instances.
<weblogic-enterprise-bean>
<ejb-name>MySession</ejb-name>
<stateless-session-descriptor>
<pool>
<max-beans-in-free-pool>50</max-beans-in-free-pool>
<initial-beans-in-free-pool>10</initial-beans-in-free-pool>
</pool>
</stateless-session-descriptor>
<enable-call-by-reference>true</enable-call-by-reference>
<jndi-name>MySession</jndi-name>
<local-jndi-name>MySessionLocal</local-jndi-name>
</weblogic-enterprise-bean>
<jonas-session>
<ejb-name>MySession</ejb-name>
<jndi-name>MySession</jndi-name>
<jndi-local-name>MySessionLocal</jndi-local-name>
<min-pool-size>10</min-pool-size>
<max-cache-size>50</max-cache-size>
</jonas-session>
Il faut noter que Weblogic propose un nombre plus important de paramètres spécifiques que Jonas, et que certains d’entre eux ne peuvent pas être traduits. Parmi ceux-là, on peut citer <enable-call-by-reference>.
EJB MDB
Pour chaque EJB MDB, un élément <jonas-message-driven> doit être ajouté. Cet élément contient le nom JNDI de la queue ou du topic auquel se branche le bean, ainsi que le dimensionnement du pool d’instances.
<weblogic-enterprise-bean>
<ejb-name>MyMDB</ejb-name>
<message-driven-descriptor>
<pool>
<max-beans-in-free-pool>50</max-beans-in-free-pool>
<initial-beans-in-free-pool>10</initial-beans-in-free-pool>
</pool>
<destination-jndi-name>jms/MyQueue</destination-jndi-name>
</message-driven-descriptor>
<dispatch-policy>IRWorkManager</dispatch-policy>
</weblogic-enterprise-bean>
<jonas-message-driven>
<ejb-name>MyMDB</ejb-name>
<jonas-message-driven-destination>
<jndi-name>jms/MyQueue</jndi-name>
</jonas-message-driven-destination>
<min-pool-size>10</min-pool-size>
<max-cache-size>50</max-cache-size>
</jonas-message-driven>
Ici aussi, certains paramètres ne peuvent pas être traduits, comme <dispatch-policy>
, qui fait référence à un <work-manager>
, dans le même fichier.
Ces paramètres permettent de gérer des pools de threads spécifiques.
A ma connaissance, le seul paramétrage équivalent dans jonas est au niveau global, dans conf/jonas.properties
, sous la clé jonas.service.ejb.mdbthreadpoolsize
.
jonas.service.ejb.mdbthreadpoolsize 10
Par ailleurs, nous avons du modifier des éléments dans le fichier ejb-jar.xml. La première concerne la durabilité de souscription du MDB, qui était spécifiée alors que le type de destination est Queue ! Ces données sont incompatibles, mais Weblogic les accepte ; Jonas les refuse et affiche un message d’erreur.
<message-driven >
<ejb-name>MyMDB</ejb-name>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
<subscription-durability>NonDurable</subscription-durability>
</message-driven-destination>
</message-driven>
<message-driven >
<ejb-name>MyMDB</ejb-name>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
</message-driven>
Les développeurs de l’application on eu la très bonne idée d’externaliser, sous forme de <env-entry>, le nom JNDI de la connexion factory pour accéder aux queues. Un <resource-ref> aurait été plus adapté, mais ne nous plaignons pas… La valeur sous Weblogic est weblogic/jms/ConnectionFactory, alors que pour Jonas, elle est CF, pour une ConnectionFactory, ou QCF, pour une QueueConnectionFactory. L’analyse du code nous a montré que c’est la deuxième fabrique qui est utilisée.
<session >
<ejb-name>MySession</ejb-name>
<env-entry>
<env-entry-name>QUEUE_CONNECTION_FACTORY_NAME</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>QCF</env-entry-value>
</env-entry>
</session>
Si le nom de la fabrique n’avait pas été externalisé, il aurait fallu créer une entrée de registre JNDI avec le même nom que sous Weblogic. Dans certains cas limités, c’est possible en ajoutant un élément <ConnectionFactory> dans conf/joramAdmin.xml ; la principale limitation de vient du fait qu’il est impossible de créer des sous-contextes (ou répertoires). Il aurait donc fallu créer un composant qui fait un lookup sur "CF" et qui fait un bind sur le nouveau nom.
Autres composants
Datasource
Comme pour les EJBs, l’utilisation du nom global de la DataSource nous impose de lui conservé son nom d’origine (jdbc/MyDS). Nous avons donc créé un fichier MyDB.properties dans le répertoire conf/ de Jonas, puis référencé ce fichier dans jonas.properties.
jonas.service.dbm.datasources MyDB
Ce fichier contient le paramétrage de la DataSource ; nous l’avons créé par copie d’un fichier existant.
datasource.name jdbc/MyDS
datasource.url jdbc:mysql://localhost/mydb
...
Log4J
La localisation du fichier de configuration de Log4J est spécifiée directement dans le code, sans externalisation ! Heureusement, le chemin est relatif, par rapport au script de lancement de Jonas. On retrouve donc ici l’intérêt de ne pas lancer Jonas directement depuis les sous-répertoires de bin, mais de créer ses propres scripts.
Il y aurait beaucoup à dire sur l’utilisation qui a été faite de Log4J. On constate une fois de plus que sa puissance ne peut pas être correctement exploitée en production à cause d’un excès de zèle des développeurs. Mais je m’éloigne du sujet…
Portage sous Jonas 5
La transition vers Jonas 5 n’a pas posé de problème supplémentaire. Seuls les noms des répertoires de déploiement ont dus être adaptés.
Conclusion
Le portage de l’application a finalement été facile à réaliser ; nous n’avons rencontré aucun point de blocage, malgré un respect lointain des préconisation de J2EE par les développeurs :
-
pas de resource-ref, ni de ejb-ref,
-
utilisation d’une QueueConnectionFactory au lieu d’une ConnectionFactory,
-
utilisation étrange de Log4J.