L3 Génie Logiciel – Intéropérabilité Java/C++, Remoting
Appels de fonctions et de méthodes passant la barrière du langage
Vous allez étudier dans ce TP les possibilités d’interopérabilité entre Java et C++, ainsi que
Java RMI.
Communication Java C++
Création d’un projet C++
Sous Eclipse, créez un projet de type « C/C++Managed Build » ou bien un projet de type
« CMake Project ». Si vous préférez créer le projet depuis un terminal, c’est aussi possible.
Projet CMake
Le projet généré contient déjà un fichier source. Vous allez générer une bibliothèque partagée
(pas un exécutable), donc votre fichier CMakeLists.txt doit être édité pour prendre la forme :
cmake_minimum_required (VERSION 3.1)
project (votreprojet)
set(CMAKE_CXX_STANDARD 11)
add_library(votreprojet SHARED votreprojet.cpp)
set_target_properties(votreprojet PROPERTIES POSITION_INDEPENDENT_CODE ON)
La dernière ligne engendre la passation de l’option –fPIC sur la ligne de commande de
g++/gcc, ce qui est requis. En l’absence de cette ligne, des erreurs ‘ld’ peuvent se produire
provoquant l’échec du build.
Projet Managed Build
Sélectionnez un projet de type « Shared Library », qui sera vide. Ajoutez ensuite un fichier
.cpp avec New > Source File, et donnez un nom de fichier finissant par ‘.cpp’.
Build depuis un terminal
Vous pouvez générer une bibliothèque partagée en faisant :
g++ –fPIC -std=c++11 votrefichier.cpp -shared -o votreprojet.so
Notez que –fPIC est obligatoire avec g++/gcc sinon une erreur ‘ld’ apparaît. De même, ne
surtout pas mettre ‘-c’ sur la ligne de commande, ce qui aurait pour effet de générer un fichier
incorrect.
Build sous Eclipse
Faites le build en cliquant sur le marteau dans la « Launch Bar ».
Ecriture du code C++
Advertisement
Ajoutez le code suivant :
[email protected] - 2019
extern "C" void test_jni(char *mystring)
{
cout << "Hello world";
}
Création d’un projet Java
Créez un projet Java et convertissez ce projet en projet Maven (clic droit sur le nœud du
projet, puis ‘Configure’ puis ‘Convert to Maven project’). Editer ensuite le fichier pom.xml (qui
apparait automatiquement) et après la section <build>, ajouter une section <dependencies>
contenant :
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
Sauvez le fichier pom.xml (Ctrl+S).
Ecriture du code Java
Deux classes doivent être créées en Java. La première contient le « proxy JNA » pour le code
natif :
// LibNative.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface LibNative extends Library
{
LibNative INSTANCE = (LibNative)Native.loadLibrary(
"..\\fc.Application.JNAJNI.Native\\build\\"
"cmake.debug.win32.x86_64\\"
libfc.Application.JNAJNI.Native.dll", LibNative.class);
Advertisement
void test_jni(String message);
}
Attention, le chemin de fichier est relatif, et doit refléter votre environnement (environnement
Windows+MinGW en mode Debug pour la ligne du dessus qu’il vous faudra changer).
La seconde classe, dans un autre fichier, contient le programme principal :
package fc.Application.JNAJNI;
public class Main
{
public static void main(String[] args)
{
System.out.println("Hello from Java");
LibNative.INSTANCE.test_jni("Hello from C++");
}
}
Lancement du programme
[email protected] - 2019
Lancez maintenant le programme Java depuis Eclipse. Si votre programme fonctionne, vous
verrez deux lignes s’afficher dans la Console d’Eclipse.
Si vous avez mal paramétré le chemin d’accès à votre bibliothèque partagée C++, vous aurez
une exception de type « UnsatisfiedLinkException ».
Examen de la pile des appels
Relancez le programme, mais en mode déboguage cette fois. Examinez ce qu’il se passe lorsque
vous faites un « step into » avec le débogueur sur la ligne contenant l’appel JNI :
Pour aller plus loin
Déclarez en Java le « miroir » d’une classe C++. Pour ce faire, déclarez en Java une classe
ayant des méthodes « native » qui appellent du code C++. Dans la classe Java, déclarez un
champ de type jlong qui vous permettra de sauver le pointeur ‘this’ de votre objet C++.
[email protected] - 2019
Communication C++ Java
Hosting de la JVM
Le principe est cette fois de créer un projet exécutable en C++ qui instancie une machine
Advertisement
virtuelle Java. On charge dans cette machine virtuelle du code Java ayant déjà été compilé. Le
code Java compilé est ensuite appelé depuis C++.
Création d’un projet C++
Créez à nouveau un projet C++, un exécutable cette fois, de préférence avec CMake car vous
allez devoir intégrer les dépendances JNI, qui sont simples à récupérer avec CMake.
cmake_minimum_required (VERSION 3.1)
project (fc.Application.JVMHost)
set(CMAKE_CXX_STANDARD 11)
find_package(JNI REQUIRED)
INCLUDE_DIRECTORIES(${JNI_INCLUDE_DIRS})
add_executable(fc.Application.JVMHost fc.Application.JVMHost)
target_link_libraries(fc.Application.JVMHost ${JNI_LIBRARIES})
Votre code dans le main va contenir la création d’une JVM :
https://pastebin.com/PrbWpXcE
Attention, ce code doit être modifié pour que la chaine java.class.path reflète votre
environnement. Cette variable passée sous forme de chaîne de caractères à la JVM indique
l’emplacement des fichiers compilés (extension .class) du projet Java que vous allez créer dans
la prochaine section (ci-après). La valeur de cette variable doit être définie de sorte que chaque
fichier .class se trouve dans des sous-dossiers correspondant à son espace de nom.
Création d’un projet Java qui va être appelé par C++
Créez un projet Java et ajouter une classe ayant pour espace de nom
fc.Application.JVMHost.Java et ayant pour nom TestJni. Cette classe ne doit exposer qu’une
seule méthode publique statique, nommée greet, et acceptant un argument de type String.
Affichez le nom reçu en paramètre dans la console.
Exécution du code C++
Lancez le programme C++ : la communication avec Java doit être assurée.
Diversification des types des arguments passés entre Java et C++
Pour aller plus loin dans la communication Java C++, passez des types autre que String,
et retournez des valeurs autre que void. Vous pouvez tester les tableaux également. Veillez à
bien libérer les données créées avec les fonctions appropriées, à bien respecter les types (jint au
lieu de int, jstring au lieu de string etc.) et à être rigoureux.
Advertisement
[email protected] - 2019
Exports de classes plus complexes
Le kit JDK installé en salles machines contient un compilateur qui permet de lire un fichier
.class et d’en générer un fichier header .H, utilisable en C/C++.
Introduction à Java RMI
Dans cette partie vous allez vous familiariser avec Java RMI, Java Remote Method Invocation.
Reprenez le code qui a été vu en cours/TD, en faisant 3 projets Eclipse distincts.
Code partagé entre le client et le serveur :
l’interface RMI doit dériver de java.rmi.Remote
la signature de la méthode distante doit comporter « throws RemoteException » à la
fin de la déclaration
Code serveur :
implémentation de l’interface, dérivant de UnicastRemoteObject
fonction main, permettant de réceptionner les appels des clients
Code client :
fonction main effectuant un lookup de l’instance distante avec Naming.lookup
appel de la méthode distante
Le code serveur doit être du type :
https://pastebin.com/cHyg5ChE
La fonction main() du serveur :
https://pastebin.com/nk4PhyAf
Une fois tout mis en place, vous testerez également comment passer une référence d’objet
distant vers le client, en ajoutant une méthode sur l’interface, renvoyant une référence d’objet
en retour. Ajoutez ce type dans le projet contenant le code partagé client/serveur.
[email protected] - 2019