Un saut dans l'univers de JUMP (Java User Migration Path)

 

Introduction

Dans un article précédent, nous évoquions l'outil Halcyon INET dont l'objectif principal consistait à convertir du code binaire MSIL afin de générer du byte-code Java. Nous étions arrivé à la conclusion que seul un outil capable de réaliser un travail minutieux au niveau des sources était réellement en mesure de générer du code pertinent.

Quelque jours après cet article, Microsoft annonçait la disponibilité de l'outil JLCA, (Java Language Conversion Assistant) autre convertisseur, mais cette fois, Java vers .NET. Au même moment, Tony Goodhew, product manager de JLCA s'exprimait dans le temple sacré de la communauté J2EE: theserverside.com et s'attirait les foudres des développeurs Java outrés par le culot affiché de Microsoft. Il ne nous en fallait pas plus pour nous intéresser à cet outil tant décrié et vérifier si réellement JLCA proposait une alternative sûre à Java. Plus généralement, c'est pour nous, l'occasion de définir clairement les liens existants entre JLCA, J# et JUMP. La plupart de ces termes marketing prêtent à confusion et tendent à semer le trouble dans les esprits. C'est pourquoi, nous allons nous attacher dans un premier temps à détailler l'ensemble de l'offre JUMP pour ensuite mettre à l'essai JLCA à travers un exemple concret de conversion. 

Description de l'offre JUMP

Officiellement, JUMP pour "Java User Migration Path" représente un ensemble de plusieurs technologies dont la vocation première est de simplifier la migration des projets Java vers la plate-forme .NET. En pratique, JUMP comprend deux produits :

1.      Le remplaçant de Visual J++ pour .NET : J#  (prononcer "J Charp")

2.      Un convertisseur de code source Java  vers C# : JLCA (Java Language Conversion Assistant) 

La figure suivante illustre les différentes étapes du processus de conversion. Vous remarquerez que JLCA travaille uniquement sur les sources, ce sera le sujet principal de cet article.

J# s'inscrit comme le remplaçant de J++ à plusieurs titres. En effet, si le Framework J++ est basé sur les APIs java et les extensions Microsoft, J# lui, utilise le Framework .NET. La communauté des développeurs a tendance à se focaliser sur l'outil de conversion Java -> C#, mais les développeurs J++ ont aussi quelques soucis à se faire car la migration des APIs J++ n'est pas forcément trivial en .NET.

Visual Studio n'intègre pas J# dans sa version finale (RTM) et, à l'heure où nous écrivons ces lignes, cet environnement n'est disponible qu'en version beta 2. 

Quant à JLCA, il est développé par une société tierce : ArtinSoft, spécialisée dans les outils de migration. Dans cette optique, un forum de discussion a été créé pour servir de support entre les équipes de développement d'ArtinSoft et les utilisateurs de JLCA : microsoft.public.vsbeta.jlca.

Plusieurs questions se posent concernant JLCA :

1.      Les projets J2SE purs peuvent-ils être migrés entièrement vers C# ?

2.      Y a t-il des restrictions concernant les applications développées en J2EE (JSP, EJB, ..) ?

3.      Comment un programme réalisé en J++ avec les WFC sera t-il converti en C# ? en utilisant l'API WinForms ?

4.      De manière plus pratique, comment fonctionne l'outil JLCA et quelles sont ses particularités par rapport au fonctionnement de produits tels que Halcyon Inet ?

Après une brève présentation de J++, nous tâcherons de répondre à l'ensemble de ces questions à travers des exemples pratiques d'utilisation de JLCA.

Différences entre J++ et J#

J++ représente l'adaptation du langage Java de Sun en version 1.1.4. En plus des APIs J2SE standards existantes, Microsoft a intégré certaines "extensions" permettant d'utiliser pleinement les ressources liées au système d'exploitation Windows :

- WFC pour Windows Fondation Classes.

- J/Connect pour l'appel de code natif (DLL).

 C'est d'ailleurs un des points de discorde entre Microsoft et Sun, ce dernier reprochant à Microsoft de briser la portabilité avec la machine virtuelle JView, seule capable d'exécuter le byte-code sous-jacent. C'est donc la raison pour laquelle J++ utilise une version obsolète du JDK, Microsoft n'ayant pu acquérir les licences des versions ultérieures.

Avec l'avènement de la plate-forme .NET, J++ a été contraint d'évoluer pour générer du byte-code MSIL. Pour cela, Microsoft a dû revoir son compilateur pour établir un lien plus étroit avec les APIs du Framework .NET (WinForms, ASP, WebServices, ..). Vous l'aurez deviné, le langage résultant est J# ou encore abusivement J++.NET.

Java Language Conversion Assistant (JLCA)

Nous avons voulu en savoir plus sur cet outil destiné à un bel avenir à en croire les quelques impressions glanées ici et là. Tout d'abord, l'installation.

L'ensemble du package JLCA est disponible sur le site de Microsoft à l'adresse suivante : http://www.microsoft.com/visualj/. L'outil se présente sous la forme d'un exécutable qui s'installe en tant que plug-in de Visual Studio tel qu'illustré sur la figure suivante :

L'étape suivante consiste à porter un programme simple, réalisé en Java, faisant référence uniquement aux APIs standards J2SE (Java 2 Standard Edition). Commençons par notre fameux helloWorld.java qui ne devrait pas poser de problème particulier aux premiers abords.

Vous remarquerez une légère variante du "HelloWorld" classique car la classe implémente l'interface java.io.Serializable. Pour bien saisir l'intérêt de la chose, rappelez-vous l'article sur Halcyon INET. L'outil avait eu du mal à reproduire à l'identique le comportement lorsqu'il a fallu générer du code Java. JLCA se comporte t-il mieux lorsqu'il s'agit de réaliser l'opération inverse ? Jugez-en par vous même. 

 

package dotnetguru.samples ;

 

public class MyHello implements java.io.Serializable

{

private String _msg ;

public MyHello(String msg)

{

       _msg = msg ;

       System.out.println("Constructeur()");

}

public void displayHello()

{

       System.out.println(_msg);

}

public static void main(String args[])

{

       MyHello h = new MyHello("Coucou EveryBody !");

       h.displayHello();

}

}

 

MyHello.java (Avant Conversion)

 

Pour effectuer la conversion, vous devrez passer les 6 étapes de l'assistant afin de renseigner divers zones : les chemins des fichiers sources et cibles, les noms de projets Visual Studio, etc ... Enfin, la sixième et dernière étape vous propose un rapport détaillé en XML sur les éventuels échecs.

Voici ce que nous donne JLCA après conversion :

namespace dotnetguru.samples

{

using System;

public class MyHello : System.Runtime.Serialization.ISerializable

{

       private System.String _msg;

       public MyHello(System.String msg)

       {

             _msg = msg;

             System.Console.Out.WriteLine("Constructeur()");

       }

      

       public virtual void  displayHello()

       {

             System.Console.Out.WriteLine(_msg);

       }

      

       public static void  main(System.String[] args)

       {

             MyHello h = new MyHello("Coucou EveryBody !");

             h.displayHello();

       }

}

}

 

Qu'en pensez-vous ? Impressionnés ? Et bien en fait, nous aurions pu nous attendre à un tel résultat. Mis à part le fait que MyHello ne constitue pas une référence en terme de complexité, cette conversion nous réconforte dans l'idée que seul le parsing de fichier source permet d'assurer un résultat satisfaisant. JLCA utilise un dictionnaire d'association de mots-clés Java et C# et se contente de remplacer les différentes occurrences. Certains parmi vous ont peut-être déjà eu à faire à l'assistant de migration VB6 vers VB.NET : JLCA fait parti de cette même famille d'outils.

Il reste encore un dernier élément que nous n'avons pas vu dans l'exemple précédent : la conversion de java.io.Serializable qui nous génère l'héritage de l'interface correspondante sous .NET : ISerializable, en lieu et place de l'attribut de Runtime [Serializable]. Problème ? Bien sûr que non, la sémantique est préservée car le comportement sous .NET est le même dans les deux cas. Bien entendu, l'attribut aurait été un choix plus judicieux mais ArtinSoft, à travers le forum JLCA, nous a assuré que ce problème serait réglé dans la version finale.

Voici le rapport généré par l'outil à la fin de l'opération de conversion :

 

Vous avez droit au nombre d'occurrences remplacées ainsi que les éventuels échecs rencontrés.

Nous allons pousser le test un peu plus loin afin de vérifier jusqu'à quel degré l'outil est en mesure de répondre à nos besoins. Sortons des sentiers battus pour migrer un programme Java utilisant les APIs JDBC, Applet, AWT et J2EE EJB.

Migration d'une application complexe (JDBC, AWT, Applets, EJB, ...)

Comme nous sommes très exigeant, la migration de code JDBC devrait logiquement générer du code équivalent basé sur les APIs System.Data de .NET. Aussi, la conversion de sources Java utilisant l'AWT (Abstract Window Toolkit) devrait nous donner des appels à System.Windows.Forms (WinForms). Et enfin, les applets Java devraient être migrées en applets .NET. Vous devez peut-être penser que nous en demandons un peu trop à l'outil ? Sans doute, mais au prix où vous les achèterez, vous serez bien content qu'ils remplissent leurs tâches sans trop d'intervention manuelle.

Voici le code source Java de notre premier jeu d'essai. C'est un programme réalisant un "select *" en SQL par l'intermédiaire de JDBC. Le code est simple et utilise les fonctions les plus importantes de l'API.

import java.sql.*;

 

public class Select{

    public static void main(String args[]){

    // Open connection to DB

      Connection ctx = ConnectionMgt.getConnection();

 

    // SQL Query

       try{

    Statement stmt = ctx.createStatement();

    ResultSet rs = stmt.executeQuery("SELECT * FROM Courses");

 

    while (rs.next()) {

       int ID = rs.getInt("ID");

       String title = rs.getString("Title");

       System.out.println("ID:"+ID+"; Title:"+ title);

    }

    stmt.close();

       }

       catch (Exception e){e.printStackTrace(); System.exit(0);}

 

    }

}

 

Select.java

 

Génère en C# :

using System;

 

public class Select

{

public static void  main(System.String[] args)

{

       // Open connection to DB

       System.Data.OleDb.OleDbTransaction ctx_transaction = null;

       // Attention, petite defaillance de l'outil : getConnection() n'existe pas

       // dans la classe OleDbConnection, nous avons dû modifier la ligne suivante

       // System.Data.OleDb.OleDbConnection ctx = ConnectionMgt.getConnection(); remplacé par

       System.Data.OleDb.OleDbConnection ctx = new System.Data.OleDb.OleDbConnection("");

      

       // SQL Query

       try

       {

             System.Data.OleDb.OleDbCommand temp_OleDbCommand;

             temp_OleDbCommand = ctx.CreateCommand();

             temp_OleDbCommand.Transaction = ctx_transaction;

             System.Data.OleDb.OleDbCommand stmt = temp_OleDbCommand;

             System.Data.OleDb.OleDbCommand temp_OleDbCommand2;

             //UPGRADE_NOTE: This code will be optimized in the future;

             temp_OleDbCommand2 = stmt;

             temp_OleDbCommand2.CommandText = "SELECT * FROM Courses";

             System.Data.OleDb.OleDbDataReader rs = temp_OleDbCommand2.ExecuteReader();

            

             while (rs.Read())

             {

                   int ID = System.Convert.ToInt32(rs[("ID")]);

                   System.String title = System.Convert.ToString(rs[("Title")]);

                   System.Console.Out.WriteLine("ID:" + ID + "; Title:" + title);

             }

             //UPGRADE_TODO: Expression java.sql.Statement.close could not be converted.;

             //stmt.close();

       }

       catch (System.Exception e)

       {

             //UPGRADE_TODO: Expression java.lang.Throwable.printStackTrace could not be converted.;

             //e.printStackTrace(); System.Environment.Exit(0);

       }

      

}

}

Select.cs (Après conversion)

 

Bluffés ? Un petit peu tout de même. Il n'a fallu modifier manuellement qu'une seule ligne du source correspondant à l'ouverture de la connexion. D'ailleurs, ce bug a été envoyé à ArtinSoft qui nous a aussitôt assuré que les modifications seraient faites pour les versions futures. Quant aux ordres TODO, elles correspondent à des travaux en cours pour étudier quelle sera la démarche à adopter pour effectuer une conversion optimale. Les APIs n'ont évidemment pas de correspondance directe dans tous les cas, il faut donc trouver des fonctions de remplacement qui brise le moins possible le modèle de départ.

Comme nous sommes joueurs et que l'outil se prête bien au test, nous allons lui proposer un fichier utilisant des APIs Servlets (JSP) et Applets. 

Mis à part le fait que les APIs Servlet traduites en C# n'ont aucun sens en .NET du fait que IIS utilise les ASP, c'est pour nous l'occasion de vérifier si, à tout hasard, elles ont été prise en compte.  Voici le source correspondant à une servlet affichant le message HelloWorld dans une page HTML générée :

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

public class HelloWorldServlet extends HttpServlet {

 

    public void doGet (HttpServletRequest req, HttpServletResponse res)

                                                throws ServletException, IOException

    {

       res.setContentType("text/html");

       ServletOutputStream out = res.getOutputStream();

       out.println("<head><title>Hello World</title></head>");

    }

 

    public String getServletInfo() {

       return "Create a page that says <i>Hello World</i> and send it back";

    }

}

HelloServlet.java

 

L'exemple de la servlet est assez significatif car si l'outil est en mesure de traduire efficacement son contenu, les pages JSP ne poseront guère de problème car toute page JSP génère une servlet. Voici le résultat :

public class HelloWorldServlet:HttpServlet

{

public virtual void  doGet(HttpServletRequest req, HttpServletResponse res)

{

       res.setContentType("text/html");

      

       ServletOutputStream out_Renamed = res.getOutputStream();

       out_Renamed.println("<html>");

       out_Renamed.println("<head><title>Hello World</title></head>");

       out_Renamed.println("<body>");

       out_Renamed.println("<h1>Hello World</h1>");

       out_Renamed.println("</body></html>");

}

public virtual System.String getServletInfo()

{

       return "Create a page that says <i>Hello World</i> and send it back";

}

}
HelloServlet.cs (Après conversion)

Pas de surprise, l'outil ne dit rien et ne fait rien, à part renommer bizarrement la variable out. Nous aurions pu nous en douter, mais vous imaginez si cela avait été possible ? Porter les sources de Tomcat ou de n'importe quel moteur de Servlets en C# et vous disposez d'une alternative aux ASP.NET liés à IIS, mais aussi d'un complément intéressant. Une servlet qui appelle dans le même processus une page ASP ou un composant .NET... arrêtons de rêver et poursuivons ;-) ...

Il nous reste à tester les APIs Applets et AWT, le programme suivant nous illustre le code source d'une applet :

import java.awt.Graphics;

 

public class MyApplet extends java.applet.Applet {

    double f(double x) {

return (Math.cos(x/5) + Math.sin(x/7) + 2) * getSize().height / 4;

    }

 

    public void paint(Graphics g) {

        for (int x = 0 ; x < getSize().width ; x++) {

    g.drawLine(x, (int)f(x), x + 1, (int)f(x + 1));

        }

    }

  public String getAppletInfo() {

    return "Draws a sin graph.";

  }

}


MyApplet.java

 nous donne après conversion :

using System;

 

//UPGRADE_TODO: The equivalent in .NET for Class java.applet.Applet will be considered in a future release.

public class MyApplet:Applet

{

internal virtual double f(double x)

{

       return (System.Math.Cos(x / 5) + System.Math.Sin(x / 7) + 2) * Size.Height / 4;

}

//UPGRADE_TODO: The equivalent in .NET for Method java.awt.Container.paint will be considered in a future release.

//UPGRADE_TODO: The equivalent in .NET for Class java.awt.Graphics will be considered in a future release.

public override void  paint(Graphics g)

{

        for (int x = 0; x < Size.Width; x++)

       {

             //UPGRADE_TODO: The equivalent in .NET for Class java.awt.Graphics.drawLine will be considered in a future release.;

             g.drawLine(x, (int) f(x), x + 1, (int) f(x + 1));

       }

}

//UPGRADE_TODO: The equivalent in .NET for Method java.applet.Applet.getAppletInfo will be considered in a future release.

public override System.String getAppletInfo()

{

       return "Draws a sin graph.";

}

}
MyApplet.cs (Après conversion)

L'outil a déjà beaucoup plus de mal à analyser et migrer l'applet en question. Il est clair que nous atteignons les limites de JLCA avec ce test, la preuve en est que les ordres TODO apparaissent pratiquement à chaque ligne, ce qui nous donne un taux d'échecs de l'ordre de 90%.

A la décharge de JLCA, il faut admettre que les versions béta se prêtent très mal aux tests intensifs en tout genre. Le but de cette release est avant tout de représenter un "proof of concept" plus qu'une vraie application de production.

Nous n'avons pas voulu tester les API J2EE/EJB au regard des faiblesses du support des API avancées, mais Microsoft nous a assuré par l'intermédiaire des forums de discussions que ArtinSoft supporterait les dernières versions du JDK (1.4?) mais aussi les API J2EE (EJB2?). Reste à savoir maintenant dans quelles mesures cette migration sera possible sans intervention manuelle. Rendez-vous dans 6 mois pour la sortie officielle ...

Conclusion

Il faut bien l'avouer, nous avons été séduit par JLCA pour plusieurs raisons. Tout d'abord, l'outil ne se prétend pas être un quelconque convertisseur "miracle" Java vers .NET. Même si ArtinSoft ne l'admet pas officiellement, ce type de produit nécessitera toujours l'intervention d'une opération manuelle aussi minime soit-elle. D'après nos tests, cette version béta ne présente pas de gain significatif concernant la migration car il reste encore énormément de blocs TODO à résoudre (~50% du code en moyenne). D'un autre coté, l'avenir joue en sa faveur car, s'il s'attache à faire correspondre fidèlement les Framework de classes de part et d'autre, JLCA sera un outil incontournable pour les déçus de Java désireux de passer à .NET. Enfin, nous pouvons regretter que Halcyon INET n'ait pas abordé la même approche dans le sens inverse (.NET vers Java) préférant privilégier la migration binaire. Il y a un marché à prendre dans ce domaine et les prétendants ne se bousculent pas au portillon, avis aux intéressés ...

 

Auteur : Sami Jaber

Copyright : DotNetGuru Ó 2002