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
|