Interopérabilité Java/.NET avec IKVM : apprendre à utiliser du code écrit en Java depuis un code C#
Un tutoriel de François DORIN

Le , par François DORIN, Responsable DotNet & Magazine
Bonjour,

Dans le billet du jour, nous allons avoir comment nous pouvons intégrer une classe, écrite initialement en Java, directement en C#, à travers un exemple concret : générer un PDF à partir de la bibliothèque Apache FOP.

Cela ne serait pas possible aussi facilement sans le projet IKVM.NET, qui est une implémentation de Java pour environnement .Net (Mono et le framework .NET de Microsoft).

Ce billet étant axé sur l'interopérabilité entre Java/.NET, je ne m'attarderai pas sur l'usage de Apache FOP.

Principe
Le principe retenu ici est assez simple.

Dans un premier temps, on va convertir le fichier .jar de la bibliothèque Apache FOP en un assembly .NET. Pour ce faire, on va utiliser l'utilitaire ikvmc qui permet de convertir le bytecode Java en assembly .NET.

Dans un second temps, on va utiliser cet assembly au sein d'un programme C#.

Préparation
Avant de procéder à la génération de l'assembly,il nous faut récupérer l'ensemble des outils nécessaires via leur site respectif :


Une fois les outils téléchargés, il faudra les extraire dans le dossier de votre choix.

Conversion .jar vers .dll
Ici, pas grand chose à faire. Si ce n'est exécuter le petit script suivant (avec quelques modifications en fonction de votre installation) :
Code : Sélectionner tout
1
2
3
4
5
 
SET IKVM_BIN_PATH=C:\Users\fdori\Downloads\ikvmbin-7.2.4630.5\ikvm-7.2.4630.5\bin 
set PATH=%PATH%;%IKVM_BIN_PATH% 
 
ikvmc -target:library *.jar ../build/fop.jar -out:fop.dll
La première partie du script permet d'indiquer où se trouve le binaire de IKVM que vous avez téléchargé à l'étape précédente. Au passage, on peut voir que pour ma part, j'utilise la version 7.2.4630.5.

La dernière ligne du script est celle qui procède effectivement à la conversion .jar -> .dll. Elle doit être exécuté dans le sous-répertoire fop/lib de l'archive contenant Apache FOP.

Les paramètres passés sont les suivants (voir doc officielle pour plus d'informations) :
  • -target: library : on indique ici que l'on souhaite généré un assembly .dll. Les autes cibles possibles sont "exe" pour une application console et "winexe" pour une application graphique ;
  • *.jar : on indique ici les fichiers à inclure. Pour des raisons de simplicité, et dans la mesure où Apache FOP vient avec l'ensemble de ses dépendances, on utilise le caractère générique * ;
  • out:fop.dll : nom du fichier de sortie, c'est-à-dire ici de l'assembly qui sera généré.


Mise en oeuvre
Maintenant que nous disposons de l'assembly fop.dll, nous pouvons l'utiliser dans un projet C# (ou VB.NET) classique. Il suffit d'ajouter à notre projet la référence de l'assembly que nous venons de générer.
Vous aurez aussi besoin de référencer des assemblys du projet IVMK. Pour Apache FOP, j'ai ûu référencer les assemblys suivants :
  • IKVM.OpenJDK.Core ;
  • IKVM.OpenJDK.XML.API ;
  • IKVM.OpenJDK.XML.Transporm.


Le premier sera nécessaire dans tous les cas, car il contient les routines de bases de l'API Java.

Et maintenant le code :

Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
     public static void GenererPDF(System.Xml.XmlDocument source, string sourceToFOPXsltFileName, string ouputFileName) 
        { 
            // Récupération du fichier XSL 
            System.Xml.Xsl.XslCompiledTransform xslt = new System.Xml.Xsl.XslCompiledTransform(); 
            xslt.Load(sourceToFOPXsltFileName); 
  
            // Génération du fichier FOP 
            string foFileName = System.IO.Path.GetTempFileName(); 
            using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(foFileName, System.Text.Encoding.UTF8)) 
            { 
                xslt.Transform(source.CreateNavigator(), null, writer); 
                writer.Close(); 
            } 
  
            // Création du PDF par interop IKVM avec ApacheFOP 
            java.io.File fo = new java.io.File(foFileName); 
            java.io.File pdf = new java.io.File(ouputFileName); 
            org.apache.fop.apps.FopFactory fopFactory = GetFactory(); 
            org.apache.fop.apps.FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); 
            java.io.OutputStream sortie = new java.io.BufferedOutputStream(new java.io.FileOutputStream(pdf)); 
            org.apache.fop.apps.Fop fop = fopFactory.newFop("application/pdf", foUserAgent, sortie); 
            javax.xml.transform.TransformerFactory factory = javax.xml.transform.TransformerFactory.newInstance(); 
            javax.xml.transform.Transformer transformer = factory.newTransformer(); 
            javax.xml.transform.Source src = new javax.xml.transform.stream.StreamSource(fo); 
            javax.xml.transform.Result res = new javax.xml.transform.sax.SAXResult(fop.getDefaultHandler()); 
            transformer.transform(src, res); 
            sortie.close(); 
        }       
  
        public static FopFactory GetFactory() 
        { 
            ResourceResolver myResolver = new MyResolver(); 
            File file = new File(fop_dir); 
            FopFactoryBuilder builder = new FopFactoryBuilder(file.toURI(), myResolver); 
            FontManager fontManager = builder.getFontManager(); 
  
            return builder.build(); 
        }
Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  
    public class MyResolver : ResourceResolver 
    { 
        public OutputStream getOutputStream(URI uri) 
        { 
            return null; 
        } 
  
        /// <summary> 
        /// Méthode appelée pour récupérer des ressources externes, comme les images 
        /// </summary> 
        /// <param name="uri"></param> 
        /// <returns></returns> 
        public Resource getResource(URI uri) 
        { 
            return null; 
        } 
    }
La classe MyResolver est une implémentation très basique et améliorable que nécessite Apache FOP pour récupérer les ressources externes.

Conclusion
Comme on peut le constater, l'utilisation de IKVM est assez simple et permet d'utiliser une bibliothèque Java en C# avec un minimum d'effort. Cette approche n'est pas encore parfaite et peut parfois rencontrer quelques bugs, mais le fait qu'un projet de la taille d'Apache FOP soit supporté est quand même très encourageant.

Il peut tout à fait être utilisé pour gérer une partie non critique d'une application (comme la génération de PDF, comme dans cet exemple).

Il existe également d'autres méthodes pour interfacer du code Java et .NET. L'approche de IKVM est intéressante dans la mesure où elle se base sur une conversion du bytecode Java en assembly. Autrement dit, il n'est pas nécessaire d'avoir une JVM d'installer pour pouvoir utiliser le code converti !


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster un commentaire

Avatar de Mickael Baron Mickael Baron - Responsable Java https://www.developpez.com
le 09/04/2018 à 9:25
Bonjour François,

Il manque une information importante est la version Java que IKVM peut supporter. Si j'ai une bibliothèque écrite en Java 9 est-ce que ça peut fonctionner ?

Merci pour le billet (diffusé sur la rubrique Java ;-))

Mickael
Avatar de François DORIN François DORIN - Responsable DotNet & Magazine https://www.developpez.com
le 09/04/2018 à 9:54
J'attends le billet dans l'autre sens : utiliser IKVM pour génerer les stub nécessaire pour appeler du code .NET en Java

Pour les versions de Java supportées, je ne trouve pas vraiment d'information. Maintenant, comme IKVM travaille directement sur le bytecode, je suppose que :
  • tant que la JVM elle-même n'est pas modifiée (pour inclure de nouvelles opérations par exemple), il n'y a pas de soucis ;
  • tant que les API utilisées sont supportées, il ne devrait pas y avoir de soucis (et c'est le cas des API de base) ;
  • tant qu'il n'y a pas d'appels natifs (JNI & co.).


Je pense que le meilleur moyen de savoir si ça marche, c'est de tester !
Avatar de jlliagre jlliagre - Modérateur https://www.developpez.com
le 09/04/2018 à 10:07
La dernière version sur laquelle le développeur d'IKVM a travaillé semble être OpenJDK 8u45, mais il a abandonné le projet depuis ...

The End of IKVM.NET
After almost fifteen years I have decided to quit working on IKVM.NET. The decision has been a long time coming. Those of you that saw yesterday’s Twitter spat, please don’t assume that was the cause. It rather shared an underlying cause. I’ve slowly been losing faith in .NET. Looking back, I guess this process started with the release of .NET 3.5. On the Java side things don’t look much better. The Java 9 module system reminds me too much of the generics erasure debacle.
Source: http://weblog.ikvm.net/
Avatar de tomlev tomlev - Rédacteur/Modérateur https://www.developpez.com
le 09/04/2018 à 10:08
Ça a l'air sympa IKVM... dommage que ce soit abandonné :S
http://weblog.ikvm.net/2017/04/21/Th...OfIKVMNET.aspx
Avatar de François DORIN François DORIN - Responsable DotNet & Magazine https://www.developpez.com
le 09/04/2018 à 13:36
Mais mais mais... comment j'ai fait pour ne pas tomber sur le blog et ne pas voir que le projet était abandoné ?

Je trouve dommage que cela n'apparaisse pas sur le site. C'est une info utile pourtant ! Merci à vous pour avoir soulevé ce "détail".
Contacter le responsable de la rubrique Accueil