Interfacer du Java avec du C (JDK 1.0.2)

L'operation est possible bien que pas automatique. L'interet d'un tel interfacage apparait si l'on dispose d'un code natif tres performant ou trop gros pour que tout soit recrit. Cependant, dans la version JDK 1.0.2 au moins, un tel interfacage releve de l'acrobatie. L'exemple ci-dessous montre comment envoyer des ordres UNIX en passant par la commande system du C.

Le principe est d'ecrire un fichier qui contient un main appelant une classe dont l'implementation est en C.

Un fichier distinct du main va decrire cette classe (appelee sur la figure Toto et dans l'exempleci-dessous Interface) qui inclut les fonctions ou procedures C . Dans cette classe, on declarera le profil des fonctions C avec un status native. Puis on chargera la librairie dans un bloc static qui n'est pas une methode.

On verifiera tout d'abord que les fichiers sont accessibles grace aux variables d'environnement :

CLASSPATH et LD_LIBRARY_PATH

Les fichiers seront compiles separement et un fichier souche special (stub) sera genere pour finir l'interfacage. On ecrit donc directement trois fichiers (deux .java et un .c).

Programme (2 fichiers differents) : 

// fichier system.java

public class system

{

    static Interface_c unix ;

    public static void main(String args[])

    {

      unix = new Interface() ;

      unix.hello() ;

      System.out.println("Je demande un <ls>.") ;

      unix.ls() ;

      System.out.println("Je demande un <pwd>.") ;

      unix.pwd();

      System.out.println("Une commande plus complexe.") ;

      unix.passe_parametre() ; // methode java qui va appeler du C

      }

      }


      // fichier Interface.java

      class Interface

      {

        // procedure C

        public native void hello() ;

        public native void ls() ;

        public native void pwd() ;

        public native void passe_param() ;

          static

          {

            // charge la librairie libma_lib.so

            System.loadLibrary("ma_lib") ;

            }

            // variables utilisees pour l'echange java/c

            public String java_string ;

            public int entier ;

            public float flottant ;

            public char caractere ;

            public void passe_parametre()

            {

              java_string = new String("Voici une chaine") ;

              entier = 10 ;

              flottant = (float) 3.14159 ;

              caractere = 'q' ;

              passe_param () ; // fonction C

              }

              }

              Il faut ensuite compiler ce programme puis generer un .h par :

              javac system.java

              javac Interface.java

              javah Interface

               Resultat

              On ecrit ensuite le fichier.c (la lecture du fichier .h genere par javah donne le nom des fonctions) : 

              /* Fichier InterfaceImp.c dont la compilation va donner un IntefaceImp.so */

              #include <StubPreamble.h>

              #include <stdio.h>

              #include "Interface.h"

              /* voir le fichier Interface.h pour le profile des fonctions C a ecrire */

              Interface_hello(struct HInterface * this)

              {

                printf("Bonjour le monde\n");

                }

                Interface_ls(struct HInterface * this)

                {

                  system("ls -l"); return;

                  }

                  Interface_pwd(struct HInterface * this)

                  {

                    system("pwd"); return;

                    }

                    void Interface_passe_param(struct HInterface * this)

                    {

                      printf(" entier : %d\n flottant : %d\n caractere : %d\n", unhand(this)->entier, unhand(this)->flottant, unhand(this)->caractere) ;

                      javaString2CString(unhand(this)->java_string, c_string, sizeof(c_string)) ;

                      printf(" Chaine : %s\n",c _string) ;

                      return ;

                      }

                      On notera qu'il faut convertir la chaine java en chaine C pour que cela fonctionne. Par ailleurs, les parametres peuvent bien sur etre modifies dans la procedure C et que pour y acceder, il a fallu passer par une macro appelee unhand.

                      Il faut ensuite creer un fichier souche qui est ecrit en C (stub file) qui va finir de realiser l'interface entre la classe Java et le programme natif :

                      javah -stubs Interface

                      Le fichier source qui a ete genere s'appelle ici Interface.c.

                      On dispose maintenant de deux fichiers .c qu'il faut compiler et mettre dans une librairie qui s'appelera ici libma_lib.so (soit en Unix SGI) :

                      cc -shared -I/opt/java/java-JDK-1.0.2/include -I/opt/java/java-JDK-1.0.2/include/irix Interface.c InterfaceImp.c -o libma_lib.so

                      Attention :

                      1. on charge ma_lib dans la classe java mais le fichier s'appelle libma_lib.so. Cette librairie doit etre accessible lors de l'execution du programme.
                      2. on ne peut pas interfacer une classe qui appartient a unpaquetage. (Ce probleme peut etre contourne en compilant tous les fichiers et en les mettant dans un repertoire accessibles a partir des variables d'environnement CLASSPATH et LD_LIBRARY_PATH, ce qui ressemble a un paquetage mais ne beaucoup moins propre).

                      En resume :

                      Fichiers sources a ecrire

                      Fichiers generes

                      librairie

                      system.java

                      Interface.java

                      InterfaceImp.c

                      system.class

                      Interface.class

                      Interface.h

                      Interface.c

                      libma_lib.so

                      Il est a priori possible de passer des parametres plutot que d'utiliser des variables de classe. Cependant, cela complique encore les choses. Enfin, il est egalement a priori possible d'appeler une methode java depuis le C mais nous n'y sommes pas arrive completement (nous attendrons le JDK 1.1 pour resoudre ce probleme).


                      Ph. RIS 1997