MokaByte 86 - Giugno 2004 
Eseguire codice nativo da applet
di
Pietro Castellucci

La situazione decritta non è così assurda come sembra, ma nella pratica può capitare spesso di dovere affrontare il problema, infatti basti pensare ad un applet che si interfaccia con uno scanner oppure uno che gestisce la firma elettronica di documenti interfacciandosi con una smartcard per leggere la chiave privata del soggetto che firma.

Il problema
Innanzitutto un'applet è in grado di eseguire codice nativo solo se ha i privilegi per farlo, rimando a [1] per maggiori dettagli su come firmare un'applet per renderla privilegiata.
In questo articolo non tratterò la scrittura delle librerie dinamiche che si vogliono eseguire, rimandando a [2] e [3] per maggiori dettagli sull'argomento.
Supponendo di avere quindi un'applet firmata o che comunque abbia i necessari diritti sulla macchina client, e supponendo di avere le nostre buone librerie C/C++ da volere eseguire, scritte e compilate in modo da poterle richiamare come metodi nativi da una classe java, ci soffermiamo su come caricare la libreria nel runtime dell'applet.

Tipicamente una libreria dinamica può essere caricata utilizzando solo due metodi, presenti sia in java.lang.System che in java.lang.Runtime, essi sono loadLibrary() e load().

Il metodo loadLibrary() prende in ingresso il nome della libreria da caricare, e va a cercare tra le librerie di sistema il nome passato, mentre load() prende in ingresso il pathname assoluto della libreria.

Come si può ben capire, nel caso di applet nessuno dei due metodi due ci dà una soluzione al nostro problema, infatti in tutti e due i casi si presume che la libreria si trovi sulla macchina dove viene eseguita l'applet, e addirittura la prima ra le librerie di sistema, però per noi la libreria può stare o nel jar che contiene l'applet oppure sul server da dove viene scaricata l'applet.

 

Soluzione
L'unica soluzione che abbiamo è quella di installare le librerie sulla macchina dove verrà eseguita l'applet, la prima volta che questa viene chiamata per poi utilizzarle ogni volta che viene usata l'applet.
Per fare questo possiamo fare due scelte, la prima è quella di utilizzare il metodo loadLibrary() e quindi di installare la libreria tra le librerie di sistema della virtual machine e la seconda è di sceglierci un pathname assoluto sulla macchina client e utilizzare load().
Nel primo caso dobbiamo trovare una directory sulla macchina client che appartiene alle directory in cui ci sono le librerie di sistema della virtual machine sula quale girerà l'applet, questo si può fare disponendo degli appropriati diritti, eseguendo il seguente codice all'interno dell'applet stessa.

private String getSystemLibraryPath(){

  // Prende dal sistema operativo su cui viene eseguita l'applet
  // il separatore di path
  String separator=java.io.File.pathSeparator;

  // Prende le librerie di sistema della virtual machine sulla quale
  // viene eseguita l'applet
  String libPath=System.getProperty("java.library.path");
  String oneLibPath="";

  // Tra tutti i path ne prende uno, il primo
  if (libPath.indexOf(separator)>=0)
    oneLibPath = libPath.substring(0,libPath.indexOf(";"));

  return oneLibPath;
}


Nel secondo caso ci scegliamo una posizione sul disco della macchina sulla quale verrà eseguita l'applet creando eventualmente la direcory

private void createDirIfNotExist(File file){
  File f=new File(path);
  if (!f.exists()) f.mkdir();
}


In tutti e due i casi dobbiamo copiare le librerie dal server alla directory se non sono già presenti, e questo possiamo farlo utilizzando il seguente metodo:

private void installFile(URL sourceUrl, String destAbsolutePathName){
File destFile=new File(destAbsolutePathName);

// Controllo che il file non esista nella destinazione specificata, solo
// in questo caso lo copio

if (!destFile.exists())
  try{
    URLConnection connection=sourceUrl.openConnection();

    InputStream is=connection.getInputStream();
    FileOutputStream fos=new FileOutputStream(destFile);
    byte[] buff = new byte[8192];
    BufferedInputStream in = new BufferedInputStream(is, buff.length);
    BufferedOutputStream out = new BufferedOutputStream(fos, buff.length);
    int i;
    int count = 0;
    while ((i = in.read(buff,0,buff.length)) != -1){
      out.write(buff,0,i);
      count += i;
    }
    in.close();
    out.close();
  }
  catch (Exception exception){
    exception.printStackTrace();
  }

}


A questo punto non ci rimane che caricare la libreria nel primo caso con loadLibrary() e nel secondo con load() e quindi utilizzarla.
Di seguito è presentato un possibile metodo init() di un'applet che vuole utilizzare una libreria nativa jtwain.dll per l'acquisizione di immagini da scanner gratuita (http://www.scavino.org)

public void init(){
  String destPath=getSystemLibraryPath();
  try{
    URL codeBase=this.getCodeBase();
    Installer.installFile(
    new URL(codeBase, "jtwain.dll"), destPath+java.io.File.separator+"jtwain.dll");
  }
  catch (Exception ecc){}
}

Per finire presento l'interfaccia java per i metodi nativi del bridge di scavino.org

public class JTwain {
  private static final JTwain mInstance = new JTwain();

  private JTwain() {
    initLib();
  }
  
  public static JTwain getInstance(){
    return mInstance;
  }

  public native boolean isTwainAvailble();

  public native String[] getAvailableSources();
  
  public native String acquire(String sourceName);
  
  private void initLib(){
    try {
      System.loadLibrary("jtwain.dll");
    }
    catch (Exception ecc){}
   }
}

 

Conclusioni
A questo punto è possibile scrivere un'applet che si interfaccia con codice nativo, e quindi con qualsiasi dispositivo hardware della macchina su cui va in esecuzione, se ha i privilegi per farlo.

 

Bibliografia
[1] A.Fumore, Firmare una applet, MokaByte, Novembre 1999
[2] E.Sordini, Java Native Interface, MokaByte, Gennaio 2000
[3] G.Rizzo, Integrazione di vecchie e nuove tecnologie attraverso Java Native Interface e J/Direct,MokaByte, Febbraio 2000


MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it