You are on page 1of 40

FAKULTET INFORMACIONIH TEHNOLOGIJA

Beograd, 2010

Integrativno programiranje softvera napisanog u 2 jezika, Java


i C++ (Integrating programing, Java/C++ integration,
Java/C++ Interface )

Predmet: SE411 Student: Vladimir Bokun


Profesor: prof. dr. Slobodan Jovanović Br. indeksa: 1151
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Sadrzaj
Rezime ................................................................................................................................................ 4
Uvod .................................................................................................................................................... 5
1. Integrativno programiranje java/C++ ................................................................................................ 6
1.1. Runtime.Exec............................................................................................................................ 7
1.2. JNI-JAVA NATIVE Interface ...................................................................................................... 9
1.2.1 Primjena JNI ........................................................................................................................ 9
1.2.2. Problemi ............................................................................................................................. 9
1.2.3. Način na koji funkcioniše JNI .............................................................................................. 9
1.2.4. Mapiranja native i Java tipova podataka ........................................................................... 10
1.2.4. Ostale primjene ................................................................................................................ 10
1.3 KNI-K Native Interface ............................................................................................................. 11
1.4. CNI-Compiled Native Interface ................................................................................................ 12
1.4.1. Java kao podskup C++ ..................................................................................................... 12
1.4.2. GCJ kompajler .................................................................................................................. 12
1.4.3. CNI framework.................................................................................................................. 12
1.5. SWIG-Simplified Wrapper and Interface Generator ................................................................. 13
1.5.1. Swig i Java ....................................................................................................................... 13
1.6. JNA-Java Native Access ......................................................................................................... 14
2. Primjer upotrebe JNI, kreiranjem projekta u Eclipse-u i Visual Studiju ........................................... 15
2.1. Ukratko o primjeru ................................................................................................................... 15
2.2. Java projekat........................................................................................................................... 17
2.2.1. Kreiranje projekta i Bean klase ......................................................................................... 17
2.2.2. Kreiranje JniExample ........................................................................................................ 19
2.2.3. Kreiranje Main klase ......................................................................................................... 21
2.3. C++ projekat (dll) .................................................................................................................... 23
2.3.1. Pozivanje javah i javap preko External Tools iz Eclipse-a ................................................. 23
2.3.2. Uključivanje generisanog header fajla u projekat .............................................................. 24
2.3.3. Implementacija klase JniExampleLibrary.cpp.................................................................... 26
2.4. Testiranje poziva izmeĎu Java projekta i C++ dll-a.................................................................. 34
2.4.1. Kompajliranje DLL-a ......................................................................................................... 34
2.4.2. Konfigurisanje putanje do dll-a u Eclipse-u ....................................................................... 34

2|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2.4.3. Pokretanje Java aplikacije ................................................................................................ 35


2.4.4. Pucanje JVM .................................................................................................................... 35
2.5. Debug Java koda i C++ DLL-a ................................................................................................ 36
2.5.1. Breakpoint Java projekta/DEBUG ..................................................................................... 36
2.5.2. Breakpoint NA C++ projektu ............................................................................................. 36
2.5.3. Debug DLL-a iz Java koda................................................................................................ 37
2.5.4. Sprječavanje pucanja JVM u primjeru ............................................................................... 37
3.Zaključak ........................................................................................................................................ 38
4. Sugestije za nastavak rada ............................................................................................................ 39
5. Literatura ....................................................................................................................................... 40

3|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

REZIME

Java okruženje i jezik je siguran i efikasan način za razvoj aplikacija. Pa ipak, ponekad je za
aplikaciju potrebna odreĎena funkcionalnost, koju Java, zbog svoje težnje da bude
multiplatfomska, nije moguce postići, ili bar ne upotrebom same Jave.
Primjer za to su:
 integracija sa postojećim bibliotekama direktno vezanim za funkcionalnosti nekog
operativnog sistema, ili hardvera
 integracija sa kodom koji je najčešće napisan u C/C++, za postizanje većih performansi, ili
bolje iskorištenje sistema
Zbog toga je razvijen standard, JNI-Java Native Interface. O njemu će biti riječi u prvom dijelu
rada, načinu na koji funkcioniše, mapiranjima izmedju C/C++ i Java tipova podataka, kao i
problemima koji nastaju upotrebom ovog API-ja.
Sem JNI, biće predstavljeni i
 KNI-K Native Inteface, namjenjen ureĎajima sa ograničenim hardverskim mogućnostima,
koji predstavlja podskup JNI,
 CNI-Compiled Native Interface, gdje se Java posmatra kao podskup C++,
 SWIG- Simplified Wrapper and Interface generator, interfejs kompajler koji povezuje
programe napisane u C i C++, sa skriptnim jezicima (Lua, Perl, Python, Ruby, Tcl) ali i
drugim, kao što je Java
 JNA- Java native access, koji omogućava Java programima lak i brz pristup native
bibliotekama, bez upotrebe JNI interfejsa.
Ponekad je potrebno samo pokrenuti neki utility program, ili drugi u okviru Java programa. Tada je
moguće koristiti Runtime.exec komandu pa će i ona biti predstavljena.
U drugom dijelu rada, biće predstavljen primjer upotrebe JNI kroz JniExample Java projekat, i C++
projekat JniExampleLibrary, odnosno pozivanje Java metoda iz C++ biblioteke, i obrnuto,
pozivanje C++ metoda iz Java projekta. Okruženja korištena za izradu ova dva projekta su
Eclipse i Visual Studio 2010.
Sem izrade, naglasak je i na debug-u projekata, a posebna pažnja posvećena je i problemima koji
mogu nastati upotrebom ovog API-ja, tj. pucanja Java virtuelne mašine izazvanim loše
napisanim C++ kodom.
Uz ovaj rad priloženi su:
 JniExample, Java projekat, uraĎen u Eclipse-u Helios (JniExample.rar)
 JniExampleLibrary, C++ Win 32 projekat, uraĎen u Visual Studiju 2010
(JniExampleLibrary.rar)

4|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

UVOD

Često se javlja potreba da iz Java virtuelne mašine treba da pristupimo nekoj native1 metodi, npr.
grafičkim funkcijama niskog nivoa, pristupu fajlovima, funkcijama mreže, i drugim rutinama, koje
zavise od operativnog sistema u kome su implementirane, ili čak niže, na nivou harvera.
Ovakve metode su napisane u native kodu, koji se kompajliranjem prevodi na mašinski jezik
razumljiv hardveru. Način na koji će ove metode biti dostupne JVM može da varira, zavisno i od
načina na koji je sama JVM implementirana. Sa ciljem da se minimizuje rad potreban za
portabilnost ovakvog koda, uspostavljen je standard JNI (Java Native Interfaces).
Namjena JNI standarda je da:
 posluži kao standardizovan interfejs ka virtualnoj mašini, tako da će iste native metode
jednako raditi pod različitim mašinama
 obezbjedi na nivou Jave univerzalni API za dinamičko učitavanje biblioteka napisanih u
native kodu i pozivanju njihovih metoda
Naravno, ovo nije jedini način da se iz Java programa pozivaju native metode, napisane u native
programima, i zavisno od tipa interakcije izmedju njih, na raspolaganju je još nekoliko API-ja.
MeĎutim, sem pozitivnih strana ovakve interakcije, ne treba zaboraviti da:
 loše napisan native kod može da obori čitavu JVM
 zbog univerzalnosti implementacije API-ja, gubi se na brzini izvršavanja, kao i korišćenja
veće količine memorijskih resursa
 dinamičko učitavanje i pozivanje native metoda iz Java programa može stvoriti sigurnosne
probleme, obzirom na nedostatak Java 2 security modela
Cilj integracije je iskorišćenje mogućnosti oba programska jezika, zavisno od potreba napisanog
Java programa, uz voĎenje računa o problemima koji mogu nastati pri ovakvoj implementaciji.

1
Native- metoda napisana u programu koji radi direktno sa hardverom, ili sa operativnim sistemom, na
niskom nivou.

5|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1. INTEGRATIVNO PROGRAMIRANJE JAVA/C++

Java je razvijena sa ciljem da omogući nezavisnost od platformi na kojima se izvršava JVM.


MeĎutim često se nalazimo u situaciji da ne možemo iznova pisati aplikaciju, već želimo da
komuniciramo sa već napisanim softverom, koji je nižeg nivoa apstrakcije od Jave (legacy
softver, operativni sistemi).
Na ovaj način se nalazimo u problemu, jer ne možemo koristiti funkcije zavisne od sistema na kom
se izvršava JVM.
Prvo moguće rješenje je pozivanjem native metoda u samom Java kodu, pri čemu gubimo na
portabilnosti napisanog softvera. MeĎutim, ukoliko želimo da komuniciramo sa native
programima na jednostavnijem nivou, gdje želimo samo pokrenuti neki utility program, ili drugu
aplikaciju, na scenu stupa Runtime klasa, odnosno njena exec metoda.
JNI-Java Native Interface je postavljen kao standard za integraciju ova dva jezika, meĎutim na
raspolaganju nam je još nekoliko API-ja, a koji ćemo izabrati, zavisi od namjene samog
programa. To su, redom:
 Runtime.Exec
 JNI
 KNI
 CNI
 SWIG
 JNA
Uz nabrojane, postoje i komercijalni API-ji, npr.
 JNIEasy
 Jnative
 JACE
Dalje ćemo predstaviti svaki od ovih API-ja, sem komercijalnih.

6|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.1. RUNTIME.EXEC
Runtime.exec je jednostavan interfejs, ka platformski zavisnim utility programima i aplikacijama. U
najprostijoj formi, primejer poziva Unix ls komande bi bio:
Process p = Runtime.getRuntime().exec("/bin/ls");
Za razliku od fork metode C-a, ne dozvoljava nam da direktno kontrolišemo okruženje. Novi proces
vrši redirekciju STDIN, STDOUT, STDERROR, pa rezultat poziva ne dobijamo na ekranu.
Umjesto toga, moramo koristiti getInputStream(), getOutputStream() i
getErrorStream()metode procesa, da bismo komunicirali sa pozvanim programom.
U listingu je dat primjer poziva komande ls:
Import java.io.*;

class execInput {
public static void main(String Argv[]) {
try {
String ls_str;

Process ls_proc = Runtime.getRuntime().exec("/bin/ls -aFl");

// get its output (your input) stream


BufferedReader br_in= new BufferedReader(new
InputStreamReader(ls_proc.getInputStream()));

try {
while ((ls_str = br_in.readLine()) != null) {
System.out.println(ls_str);
}
} catch (IOException e) {
System.exit(0);
}
} catch (IOException e1) {
System.err.println(e1);
System.exit(1);
}

System.exit(0);
}
}

Od verzije JDK 1.1 PATH varijabla je poznata, ali je i pored toga dobra praksa proslijeĎivati punu
putanju do programa koga pokrećemo.
Shell ugradjene komande operativnog sistema neće raditi u svim slučajevima, bar ne na
jednostavan način koa što smo navikli. Npr. za poziv DOS dir komande, poziv bi bio
command \c dir
Ukoliko želimo da proslijedimo više parametara za redom, umjesto pisanja komande u stringu i
proslijeĎivanja exec metodi, potrebno je napraviti komandu kao niz stringova, i takav niz predati
za argument.
Postoje četiri overloaded exec metode klase Proces, redom:

7|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

 public Process exec(String command);


 public Process exec(String [] cmdArray);
 public Process exec(String command, String [] envp);
 public Process exec(String [] cmdArray, String [] envp);

Trošak upotrebe ove komande je što gubimo na portabilnosti napisanog Java softvera, dok je sa
druge strane najjednostavniji način da koristimo, i pokrećemo alate i utility-je operativnog
sistema, ali i druge programe. Česta primjena kod Java programera je upotreba ove komande
za pozivanje eksterno napisanih Help stranica (.chm i sl.) za aplikacije iz kojih se pokreću.

8|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.2. JNI-JAVA NATIVE INTERFACE


JNI je univerzalni interfejs koji dozvoljava Java kodu u okviru JVM da poziva, i bude pozivan od
strane native aplikacija (programa zavisnih od harvera na kom se izvršavaju i operativnih
sistema), i biblioteka napisanih u C/C++ i mašinskom jeziku.
Na taj nacin rješavaju se problemi u potrebi za funkcionalnošću koja nedostaje Javi, u komunikaciji
na nižem nivou od JVM, a koja je žrtvovana u ime portabilnosti Jave meĎu različitim
platformama.

1.2.1 PRIMJENA JNI


Čak i mnoge standardne Java biblioteke koriste JNI, i na taj način isporučuju odgovarajuće
funkcionalnosti, npr. metode za rad sa fajlovima i sa zvukom.
JNI omogućava native metodama da koriste Java objekte, na način na koji se to čini i u Java
programima, dakle, native metod može instancirati Java objekte, ili primati kao parametre
objekte koji su već instancirani u Java programu, raditi sa njima, pozivati njihove metode, i
mjenjati ih.
Sa druge strane, često se javlja potreba da već imamo native kod napisan, i dostupan preko
biblioteka, i Javu koristimo kao interfejs za pristup tim metodama, odnosno funkcionalnostima
koje ove biblioteke pružaju.
Ukoliko se radi o vremenski zahtjevnoj operaciji, obzirom na niži nivo apstrakcije native koda,
prevedenim u mašinski kod i sposobnim da iskoristi sve prednosti hardvera na kom se izvršava,
jasno je da će ovakav kod izvršavati brže, npr. kompleksni matematički obračuni. U novije
vrijeme, ova razlika je sve manja, jer se JVM usavršava, pa je danas moguće da ce se u nekim
slučajevima JVM bajt kod izvršavati brže od ekvivalentnog native koda.

1.2.2. PROBLEMI
Postoji niz problema sa kojima se suočavamo, upotrebom JNI, kao npr.:
 greške u upotrebi JNI mogu destabilizovati čitavu JVM, a koje je ponekad teško otkriti ili
debug-ovati
 samo aplikacije i potpisani apleti mogu koristiti JNI
 aplikacija gubi na portabilnosti upotrebom JNI, moguće rješenje je da napišemo posebnu
implementaciju za svaku platormu, a onda da koristimo AbstractFactory pattern, zavisno od
platfome
 JNI ne obezbjeĎuje Garbage Collector, za resurse koji nisu u direktnoj vezi sa JVM, znači
moramo voditi računa o svim objektima instanciranim u okviru native metoda, i njihovom
uništavanju
 provjera grešaka je obaveza pri ovakvoj implementaciji, obzirom da ove greške mogu
oboriti čitavu JVM
koristi se modifikovani UTF-8, u metodama JNI poput NewStringUTF i sl.

1.2.3. NAČIN NA KOJI FUNKCIONIŠE JNI


Kada JVM poziva native funkciju, prosijeĎuje se JNIEnv pokazivač, tipa jobject, i argumenti
funkcije deklarisani od strane Java metode . Ovaj pokazivač je struktura koja sadrži interfejs ka
JVM, odnosno svim neophodnim metodama za interakciju sa JVM i rad sa Java objektima. U

9|Strana
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

suštini, bilo što što Java kod može da uradi, može da se uradi i preko JNIEnv, ali na dosta
komplikovaniji način.
Primjer upotrebe je instanciranje objekata, kreiranje stringova, i sl., o čemu ce biti riječi u drugom
dijelu izlaganja. Kod napisan u C++ koji koristi JNI je nešto čistiji u odnosu na C, jer je C++
objektno orjentisan jezik, poput Jave.

1.2.4. MAPIRANJA NATIVE I JAVA TIPOVA PODATAKA


Native tipovi se mogu mapirati na Java proste tipove podataka, i obrnuto kao u sledećoj tabeli. Za
druge tipove, potpis je po obrazcu L<naziv paketa>.<naziv klase>;, npr. za String
Ljava/lang/String;

Ukoliko se radi o nizu koristi se notacija [ ispred potpisa tipa, npr. za niz cjelih brojeva, pišemo [I.
Tabela 1. Mapirani tipovi
Native tip Java Tip Opis “Signature”
tipa

unsigned char jboolean neoznačenih 8 bita Z

signed char jbyte označenih 8 bita B

unsigned short jchar neoznačenih 16 bita C

short jshort označenih 16 bita S

int Jint označenih 32 bita I

long jlong označenih 64 bita J

float jfloat 32 bita F

double jdouble 64 bita D

Ovi tipovi takoĎe podrazumjevaju implicitnu konverziju meĎusobno ekvivalentnih Java/native tipova
npr. jint nije potrebno konvertovati u int da bi se koristio kao int. Ovo ne važi uvijek, npr. za
stringove, upotreba jstring na mjestu char * dovešće do pucanja JVM. Izuzetak od pravila
su i nizovi, gdje je potrebno koristiti posebne konstrukcije, kao što ce biti navedeno u primjeru
kasnije.

1.2.4. OSTALE PRIMJENE


Ne samo da native i Java kod mogu da se zajedno koriste, vec je moguće i crtati direktno na Java
Canvas objekat, upotrebom Java AWT native interfejsa. Sem toga, moguće je i direktno
pristupati assembly kodu, i obrnuto iz assembly koda pristupati Java aplikaciji.

10 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.3 KNI-K NATIVE INTERFACE


Zbog univerzalne namjene JNI, često se ovaj API postavlja kao previše skup, po pitanju utroška
memorije, i performansi. Uz to, pojavljuju se i sigurnosni problemi, obzirom na nedostatak Java
2 security modela.
Cilj KNI je da izdvoji podskup funkcionalnosti JNI, koji su odgovarajući za ureĎaje sa ograničenom
memorijom, skromnijih hardverskih mogućnosti. Obzirom da je namjenjen da bude lakši po
pitanju zahtjeva, prati konvencije JNI koliko je to u duhu samog API-ja, dok je konvencija
proslijeĎivanja parametara kompletno redizajnirana.
Glavni ciljevi KNI su:
 Portabilnost native koda na nivou source koda –za razliku od JNI, sa binarnom
portabilnošću i vec kompajliranim kodom. To znači da se isti source kod native metoda
može korisiti na više JVM bez modifikacija, a koji nije specifičan za operativni sistem.
 Izolacija detalja implementacije JVM, od native metoda-kada piše native kod,
programer ne mora ništa da zna o strukturi klasa Java programa, načinu uništavanja
objekata, zavisnim fajlovima..
 Efikasno korištenje memorije i minimalno obaranje performansi-naglasak na
kompaktnosti i efikasnosti u odnosu na punu JNI implementaciju.
KNI je logički podskup JNI API-ja. Najznačajnije razlike su:
 KNI je API na implementacionom nivou-za razliku od JNI, prisustvo KNI je u potpunosti
nevidljivo Java programeru.
 Nema binarne kompatibilnosti native koda-KNI ne garantuje da će binarni kod koji je već
kompajliran na jednoj VM, biti podržan od strane druge VM, bez rekompajliranja. Potrebno
je obezbjediti kompatibilnost samo na nivou source koda.
 Nema podrške za dinamičko učitavanje native biblioteka-KNI ne obezbjeĎuje
mehanizme za dinamičko učitavanje native biblioteka u VM.
 Nema pristupa od strane Jave native funkcijama, sem onih koje su već dodate VM.
 Nema kreiranja klasa, instanciranja, ili pozivanja Java metoda od strane native
funkcija-da bi se API održao malim, i manje podložnim greškama, instanciranje je
prepušteno Java programu, a potom se objekat može predati native metodi kao parametar.
 ProslijeĎivanje parametara-KNI koristi pristup baziran na registrima, u kom argumenti
mogu biti pročitani direktno sa steka, nezavisno od implementacije, i za razliku od starog
KMV interfejsa eksplicitno postavljanje i skidanje parametara sa steka nije dozvoljeno.

11 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.4. CNI-COMPILED NATIVE INTERFACE

1.4.1. JAVA KAO PODSKUP C++


Ako gledamo iz ugla mogućnosti jezika, Javu možemo posmatrati kao podskup C++. Iako Java ima
nekoliko značajnih ekstenzija, uz moćnu standardnu biblioteku klasa, to ne mjenja osnovnu
sličnost ova dva jezika. Java je hibridni objektno orjentisani jezik, sa nekoliko osnovnih tipova uz
dodatak klasnih tipova. Objekti se alociraju na heap-u, a inicijalizuju preko specijalnog metoda
konstruktora. Klase se organizuju u pakete.

1.4.2. GCJ KOMPAJLER


GCJ ili GNU Compiler for Java, je besplatan kompajler, i dio kolekcije GNU kompajlera. Ima
mogućnost kompajliranja Java source koda u Javin bajt kod, ili mašinski kod direktno, za bilo
koju od različitih CPU arhitektura.
GCJ ima mogućnost kompajliranja već kompajliranih class fajlova, koji sadrže bajt kod ili citavih
jar-ova.
Java kod kompajliran u mačinski kod od strane GCJ trebao bi da se pokrene brže od ekvivalentnog
pokrenutog na JVM. MeĎutim, poslije pokretanja programa, ne mora da znači da će se ovakav
kod izvršavati brže od sličnog na JVM, ukoliko se radi o modernoj JVM, sa JIT 2.

1.4.3. CNI FRAMEWORK


CNI je softverski framework koji omogućava Java kodu da poziva i bude pozivan od native koda, i
biblioteka napisanih u C++. Sličan je JNI, meĎutim autori ističu da se radi o boljem pristupu.
Implementacija je bazirana na ideji da je Java još samo jedan programski jezik, koji se može
prevesti u mašinski jezik standarnim tehnikama, odnosno standardnim kompajlerima poput
GCC. Java se posmatra kao podskup C++, a CNI kao skup helper funkcija i konvencija izgraĎen
oko ideje da C++ i Java dijele iste konvencije pozivanja i objektni layout.
Za datu Java klasu lijevo dat je primjer upotrebe u C++ desno.

public class Int { #include <gcj/cni.h>


public int i; #include <Int>
public Int (int i) { this.i = i; }
public static Int zero = new Int(0); Int* mult(Int* p, jint k)
} {
if (k == 0)
return Int::zero; // Static member
access.
return new Int(p->i * k);
}

2
JIT-Just-in-ime compilation, metod za povećanje performansi pokrenutog programa, poznat i kao
dynamic translation.

12 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.5. SWIG-SIMPLIFIED WRAPPER AND INTERFACE GENERATOR


SWIG je interfejs kompajler koji povezuje programe napisane u C i C++, sa skriptnim jezicima
(Lua, Perl, Python, Ruby, Tcl). Radi tako što uzima deklaracije pronaĎene u C/C++ header
fajlovima, i koristeći njih kreira wrapper kod koji je potreban skriptnom jeziku da pristupi
željenom C/C++ kodu. SWIG se može posmatrati i kao RAD3 alat. Sem rada sa skriptnim
jezicima, podrzava i druge jezike (Java, C#, Modula-3).
Dva su glavna razloga za povezivanje skript jezika i C/C++ programa:
 Program se može mijenjati brže, putem skripta, umjesto pisanjem C/C++. Script engine
može biti izložen i krajnjem korisniku za upotrebu, tako da on može automatizovati neke
radnje koje se ponavljaju, pisanjem skriptova.
 Čak i kada krajnji proizvod ne sadrži ovaj engine, može se koristiti za pisanje testova.

1.5.1. SWIG I JAVA


SWIG generiše JNI kod za nas. Razlika izmeĎu upotrebe javah i SWIG-a je sto SWIG uzima već
napisani C/C++ kod, i kreira wrapper za njega, dok javah polazi od Java deklaracija native
funkcija i kreira C/C++ prototipove. Pri tome koristi Java proxy klase, i vrlo je upotrebljiv u
slučajevima da želimo da imamo pristup velikoj količini funkcionalnosti C/C++ biblioteka i
programa, iz Java programa. Ukoliko želimo pristup samo jednoj ili dvije metode u nekoj
biblioteci, upotreba SWIG-a nema smisla.

3
RAD-Rapid Application Development
13 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

1.6. JNA-JAVA NATIVE ACCESS


JNA omogućava Java programima lak i brz pristup native bibliotekama, bez upotrebe JNI
interfejsa. Cilj je pojednostavljenje upotrebe biblioteka, bez generisanja dodatnog koda.
JNA koristi malu native biblioteku, koja je nazvana foreign function interface (libffi) da dinamički
poziva native kod. JNA biblioteka koristi native funkcije, koje dozvoljavaju kodu da učita
biblioteku po imenu, vraća pokazivač na funkciju u okviru te biblioteke, i poziva je, bez statičkog
vezivanja i upotrebe header fajlova, niti dodatnog kompajliranja C++ dll-a. Programer koristi
Java interfejse da opiše funkcije i strukture u ciljanoj native biblioteci.
U primjeru je portabilan kod izmeĎu platformi Windows, Unix, Linux, Mac OS X. U slučaju da se
radi o Windows-u, učitava se dinamički msvcrt biblioteka, a za ostale operativne sisteme c
biblioteka. VoĎeno je računa o portabilnosti, meĎutim da ušitavamo samo msvcrt kod bi bio
upotrebljiv samo pod Windows-om.

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

/** Simple example of native library declaration and usage. */


public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary(
(Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
}

public static void main(String[] args) {


CLibrary.INSTANCE.printf("Hello, World\n");
for (int i = 0; i < args.length; i++) {
CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
}
}
}

Trenutno je JNA moguće koristiti na Mac OS X, Microsoft windows-u, FreeBSD/OpenBSD,


Solarisu i Linuxu.

14 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2. PRIMJER UPOTREBE JNI, KREIRANJEM PROJEKTA U ECLIPSE-U I VISUAL


STUDIJU

2.1. UKRATKO O PRIMJERU


JNI dozvoljava komunikaciju izmeĎu JAVA programa i native4 programa (napisanog u C,C++). U
primjeru ćemo se osvrnuti na ovu tehnologiju, počev od podešavanja okruženja (Eclipse, Visual
Studio), preko kreiranja programa uz upotrebu JNI, debug native i Java koda, nastojati da se
izbjegnu tzv. curenja memorije, i razumiju rizici pucanja JVM mašine.
Dakle, cilj primjera je:
 da se podesi okruženje, koristiće se Eclipse za pisanje Java koda i Visual Studio, za
pisanje native koda u C++.
 kreiranje programa uz upotrebu JNI
 debug izmeĎu native koda, i koda napisanog u Javi
 da se izbjegnu curenja memorije
 da se razumiju rizici od pucanja JVM

Potrebno za ovaj primjer je Eclipse razvojno okruženje, dostupno na www.eclipse.org , i Visual


Studio (dovoljna će biti verzija Express koja je besplatna) koga je moguće downloadowati sa
sajta www.microsoft.com/express .

U primjeru se koriste dvije Java klase Bean i JniExample. JniExample učitava native biblioteku (
JniExampleLibrary.dll5) i otvara Java klasi metode koje su implementirane u dll-u. Bean se
koristi da se prikaže kako se razmjenjuju podaci putem JNI.

Prvo ćemo koristiti javah.exe da generišemo header fajl koji deklariše native metode u dll-u, kao
što smo definisali u JniExample.java. Komanda koju je potrebno pokrenuti je:

javah -d E:\dev\cpp\JniExampleLibrary\ JniExampleLibrary


rs.edu.fit.se411.jni.JniExample

Da bi kreirali potpise metoda, koristićemo i javap.exe, na sledeći način:

javap -s rs.edu.fit.se411.jni.JniExample

Kako će se program vremenom mjenjati, preporuka je da se ove komande sačuvaju u okviru


Eclipse pod External Tools.

4
Native program-Napisan za hardver, ili za operativni sistem, na niskom nivou, kompajliranjem prevodi se u
mašinski kod.
5
Dll-Dynamic-link Library, je Microsoft implementacija za koncept djeljene biblioteke, u MS Windows
operativnim sistemima, i OS/2. Moguće ekstenzije su DLL, OCX-ActiveX kontrole, koje pamtimo još iz
vremena Visual Basic 6 i njihove registracije, i DRV (sistemski drajveri).
15 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Za debug biblioteke, i Java programa istovremeno, u okviru Visual Studija treba poći na Debug,
potom Attach to Process, pronaći javaw.exe i odabrati Attach komandu.
Kao i kod bilo kod native koda, potrebno je obratiti posebnu pažnju na moguća curenja memorije
(jer nemamo Garbage Collector da to uradi za nas). Ipak, u slučaju JNI, JVM obavlja većinu
posla za nas. Bilo koja instanca proslijedjena ili vraćena od strane dll-a, biće počisćena od
strane JVM. Potrebno je da oslobodimo bafer, samo ako je alociran objekat van JVM.
Na kraju, prikazaćemo i kako da dovedemo do pucanja JVM, što će biti iznenaĎujuće lako. Ukoliko
imamo namjeru da u nasoj web aplikaciji koristimo i native kod, treba da budemo svjesni da
problem loše napisanog i nesigurnog koda u native biblioteci može oboriti čitav server. Zbog
toga bi bilo dobro da se native biblioteka pokreće na posebnom web serveru.

16 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2.2. JAVA PROJEKAT

2.2.1. KREIRANJE PROJEKTA I BEAN KLASE


Pokrenimo Eclipse, i započnimo novi Java projekat. Postavimo naziv projekta na JniExample, kao
na slici.

Slika 1.Kreiranje projekta

Potom treba kreirati novu klasu, Bean, u paketu rs.edu.fit.se460.jni

Slika 2.Kreiranje klase Bean

17 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

dodati joj dva podatka člana,


public String dataString;
public byte[] dataByteArray;
i enkapsulirati ih (dodati getere i setere). Uz to dodaćemo i metod toString, koga ćemo koristiti
kasnije. Cio kod ove klase je ispod.

package rs.edu.fit.se411.jni;

public class Bean {

//public members
public String dataString;
public byte[] dataByteArray;

public Bean() {}

//getters and setters


public String getDataString() {
return dataString;
}

public void setDataString(String dataString) {


this.dataString = dataString;
}

public byte[] getDataByteArray() {


return dataByteArray;
}

public void setDataByteArray(byte[] dataByteArray) {


this.dataByteArray = dataByteArray;
}

@Override
public String toString(){
String ret = "string = " + dataString;

ret += " / byteArray =";


if ( dataByteArray != null ) {
for ( byte b : dataByteArray) {

18 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

ret += " " + b;


}
}

return ret;
}
}

2.2.2. KREIRANJE JNIEXAMPLE


Kreiraćemo još jednu klasu, i nazvati je JniExample. Ovom klasom ćemo učitati dll napisan u C++.
Za to, možemo koristiti System.loadLibrary, sa nazivom dll-a kao parametrom, tj.
System.loadLibrary("JniExampleLibrary");
Sada ćemo dodati četiri native metoda, koja ćemo kasnije implementirati u dll-u. Oni se razlikuju od
običnih Java metoda po native ključnoj riječi u potpisu metoda.
public native void callJavaMethod();
public native Bean createAndReturnBean();
public native void modifyBean(Bean bean);
public native void crashTheJvm();
O ovim metodama bice jos riječi kasnije, kada budemo pisali njihovu implementaciju u dll-u. Za
sada, recimo samo da će callJavaMethod pozivati metod napisan u ovoj klasi,
printSomething, createAndReturnBean će kreirati i vratiti Bean Java klasu koju smo
prethodno kreirali, modifyBean ce uzeti Bean kao parametar, i izmjeniti ga, a crashTheJvm
ce kodom iz dll-a izazvati pucanje JVM.
Osim ovih native metoda, u klasi JniExample imamo i implementaciju metoda printSomething,
kao i public metode:
 runExample1
 runExample2
 runExample3
 runExample4
koje će redom pozivati navedene native metode, i testirati ih. Listing ove klase je ispod.

package rs.edu.fit.se411.jni;

public class JniExample {

static {
System.loadLibrary("JniExampleLibrary");
}

// native methods

19 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

/**
* this method calls printSomething
*/
public native void callJavaMethod();

/**
* this method creates an instance of Bean and returns it
*/
public native Bean createAndReturnBean();

/**
* this method takes an instance of Bean as parameter and changes the
value of its members
*/
public native void modifyBean(Bean bean);

/**
* this method will the JVM when invoked
*/
public native void crashTheJvm();

public void printSomething() {


System.out.println("Thanks for watching this");
}

public void runExample1() {


System.out.println("starting runExample1...");
callJavaMethod();
}

public void runExample2() {


System.out.println("starting runExample2...");

Bean bean = createAndReturnBean();


System.out.println("returned= " + bean.toString());
}

public void runExample3() {


System.out.println("starting runExample3...");

Bean bean = new Bean();

20 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

bean.setDataString("hello");

byte[] byteArray = new byte[] { 0x01, 0x02, 0x03 };


bean.setDataByteArray(byteArray);

System.out.println("before: " + bean.toString());


modifyBean(bean);
System.out.println("after: " + bean.toString());
}

public void runExample4() {


System.out.println("starting runExample4...");
crashTheJvm();
}
}

2.2.3. KREIRANJE MAIN KLASE


Sad treba kreirati i polaznu tačku aplikacije6, Main klasu.

Slika 3.Kreiranje klase Main

6
Entry Point-polazna tačka aplikacije
21 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

U njoj ćemo pozivati public metode iz JniExample, redom, kao u listingu ispod.

package rs.edu.fit.se411.jni;

public class Main {

/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Starting JavaMain...");

JniExample jniExample = new JniExample();


jniExample.runExample1();
jniExample.runExample2();
jniExample.runExample3();
//jniExample.runExample4();
}

Za potrebe testiranja poziv runExample4 ce biti zakomentarisan, i testiran poziv dll-a sa prve tri
metode, pa potom testiran i sa odkomentarisanim pozivom ove metode, za test pucanja JVM.

22 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2.3. C++ PROJEKAT (DLL)


Pokrenimo Visual Studio (u primjeru koristen VS 2010). Potrebno je kreirati novi projekat, koga
ćemo nazvati JniExampleLibrary, tip ovog projekta je Win32 Project, potom je na wizzard-u
potrebno odabrati DLL, i Empty Project, kao na slici.

Slika 4.Kreiranje dll-a JniExampleLibrary

U Source Files logičkom folderu dll projekta kreiraćemo cpp klasu JniExampleLibrary.

2.3.1. POZIVANJE JAVAH I JAVAP PREKO EXTERNAL TOOLS IZ ECLIPSE-A


Da bismo generisali C++ header fajl, sa potpisom svih native metoda koje se nalaze u JniExample
Java klasi, koristićemo alat javah.
Još jedan alat koji ćemo koristi je javap, koji štampa signature svih native metoda u konzoli. I javah
i javap se nalaze u bin folderu instaliranog JDK na računaru, u primjeru C:\Program
Files\Java\jdk1.6.0_18\bin.
Za pozivanje ovih alata možemo koristiti konzolu, meĎutim uzmemo li u obzir da ćemo kod
najvjerovatnije vremenom mjenjati, odlučićemo se za fleksibilnije rješenje, dodavanjem u Run-
>External Tools->External Tools Configuration.. novog programa, kao na slici ispod.

23 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Slika 5.Create HeaderFile.. u External Tools Eclipse-a

Pošto popunimo novi program podacima kao na slici, prvo idemo na Apply, pa Run. Ukoliko je sve
u redu, u folderu C++ projekta će se naći novi header fajl, rs_edu_fit_se411_jni_JniExample.h.

2.3.2. UKLJUČIVANJE GENERISANOG HEADER FAJLA U PROJEKAT


Uključimo ovaj header fajl u C++ projekat. Da bismo to uradili idemo na logički folder Header Files,
i desnim klikom misa Add..
Listing generisanog fajla je ispod.

/* DO NOT EDIT THIS FILE - it is machine generated */


#include <jni.h>
/* Header for class rs_edu_fit_se411_jni_JniExample */

#ifndef _Included_rs_edu_fit_se411_jni_JniExample
#define _Included_rs_edu_fit_se411_jni_JniExample
#ifdef __cplusplus
extern "C" {

24 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

#endif
/*
* Class: rs_edu_fit_se411_jni_JniExample
* Method: callJavaMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_callJavaMethod
(JNIEnv *, jobject);

/*
* Class: rs_edu_fit_se411_jni_JniExample
* Method: createAndReturnBean
* Signature: ()Lrs/edu/fit/se411/jni/Bean;
*/
JNIEXPORT jobject JNICALL Java_rs_edu_fit_se411_jni_JniExample_createAndReturnBean
(JNIEnv *, jobject);

/*
* Class: rs_edu_fit_se411_jni_JniExample
* Method: modifyBean
* Signature: (Lrs/edu/fit/se411/jni/Bean;)V
*/
JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_modifyBean
(JNIEnv *, jobject, jobject);

/*
* Class: rs_edu_fit_se411_jni_JniExample
* Method: crashTheJvm
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_crashTheJvm
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

2.3.2.1. Uključivanje JNI DLL-ova u Visual Studio


Pri uključivanju ovog header fajla, pojaviće se greške u Visual Studiju. Razlog je što Visual Studio
ne zna za JNI, niti dll-ove koji omogućavaju integraciju C++ i Java.

25 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Zbog toga označimo projekat u Visual Studiju, potom desni klik mišem na Properties, pa C/C++,
General u stablu, i na učitanoj stranici primjetimo prvi red Additional Include Directories. Idemo
na <Edit..>, i dodamo sledeće dvije putanje:
 C:\Program Files\Java\jdk1.6.0_18\include
 C:\Program Files\Java\jdk1.6.0_18\include\win32

Slika 6. Additional Include Directories

2.3.3. IMPLEMENTACIJA KLASE JNIEXAMPLELIBRARY.CPP


Uključimo header fajl u implementacionu klasu JniExampleLibrary.cpp na sledeci nacin.
#include "rs_edu_fit_se411_jni_JniExample.h"
#include <cstring>
Potrebno je implementirati sve metode iz header fajla, u ovoj klasi, zbog toga implementirajmo ove
metode, za sada sa praznim tijelom (bez ikakvog koda).

2.3.3.1. Poziv javap za generisanje signature-a metoda


Prije nego nastavimo sa pisanjem C++ koda, moramo da vidimo ove signature-potpise native
metoda. Zbog toga ćemo u Eclipse-u dodati u External Tools dodati novi program, koji poziva
javap za ovu klasu JniExample.
Vidjećemo kroz listing ove klase zbog čega su nam potrebni signature ovih metoda.

26 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Na sličan način kao što smo dodali javah, dodajmo i program javap.

Slika 7.CreateJniExample Signatures.. u External Tools Eclipse-a

Potom idemo na Apply i Run. Isti rezultat možemo dobiti i iz konzole (moramo voditi računa da je
postavljen JAVA_HOME, ili pokrenuti iz bin foldera JDK-a komandu).

Slika 8.javap pokrenut iz CMD-a

27 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Vidimo da su za void native metode siganture ()V, dok je za metodu koja vraća Bean taj potpis
drugačiji, tj. ()Lrs/edu/fit/se411/jni/Bean;

2.3.3.2. CallJavaMethod()
U ovoj metodi će se pozvati Java metoda printSomething (iz klase JniExample). Objasnićemo
red po red ovog listinga.
Metod JniEnv instance GetObjectClass nam vraća handler na klasu JniExample.GetMethodID
nam vraća handler na metod printSomething, čiji je signature ()V. Na kraju
CallVoidMethod rezultuje pozivanjem Java metoda klase JniExample, koje dobija ovaj metod
kao parametre.

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_callJavaMethod


(JNIEnv * env, jobject obj)
{
jclass jniExampleCls = env->GetObjectClass(obj);

jmethodID mid = env->GetMethodID(jniExampleCls, "printSomething",


"()V");

env->CallVoidMethod(obj, mid);
}

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_callJavaMethod


(JNIEnv * env, jobject obj)
{
jclass jniExampleCls = env->GetObjectClass(obj);

jmethodID mid = env->GetMethodID(jniExampleCls, "printSomething", "()V");

env->CallVoidMethod(obj, mid);
}

2.3.3.3. CreatAndReturnBean()
Ovaj metod je zadužen za kreiranje i vraćanje Bean Java instance.
Prvo nalazimo klasu Bean. Za nju, potrebno je kreirati instancu, tj. pokrenuti konstruktor. U JNI, to
radimo preko <init>, odnosno sa GetMethodID(beanClass, "<init>", "()V") nalazimo
konstruktor, i u sledećoj liniji pozivamo sa NewObject.

28 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Potom kreiramo UTF string, u JNI to radimo preko NewStringUTF. Sada je potrebno Bean instanci
setovati podatak član dataString preko settera setDataString, pa nalazimo handler na
setDataString, čiji je signature (Ljava/lang/String;)V.

Kako je seter void tipa, zovemo CallVodMethod i proslijeĎujemo string seteru. Povratna
vrijednost u JNI je jobject i to je instancirani Bean sa setovanim stringom.

JNIEXPORT jobject JNICALL Java_rs_edu_fit_se411_jni_JniExample_createAndReturnBean


(JNIEnv * env, jobject obj)
{
jclass beanClass = env->FindClass("rs/edu/fit/se411/jni/Bean");
jmethodID constructorMethodId = env->GetMethodID(beanClass, "<init>", "()V");
jobject bean = env->NewObject(beanClass, constructorMethodId);

jstring newString = env->NewStringUTF("this bean has been created in


Java_codebazaar_example_JniExample_createAndReturnBean");

jmethodID setStringMid = env->GetMethodID(beanClass, "setDataString",


"(Ljava/lang/String;)V");

env->CallVoidMethod(bean, setStringMid, newString );

return bean;
}

2.3.3.3. ModifyBean()
U ovom primjeru ćemo uzeti postojeći Bean, kao parametar, vec instanciran u okviru neke Java
klase, i u C++ kodu izmjeniti njegove podatke članove. U Java klasi ćemo za testiranje prije
izmjene, i poslije, koristi pomoćni metod toString, da bi smo vidjeli razliku u podacima prije i
poslije dejstva ove metode.
Prvo nalazimo klasu, za parametar tipa jobject. Potom nalazimo handler na setDataString,
kreiramo novi string, i pozivamo void metod sa stringom kao parametrom, slično prethodnom
primjeru.
Dalje ćemo izmjeniti još jedan podatak član, koji je JNI tipa jbyteArray, odnosno polje Bean-a
dataByteArray. Najprije deklarišemo newByteArray tipa jbyteArray, dimenzije pet bajtova,
pa instanciramo niz od pet jbyte-ova, a potom ovaj niz upisujemo u deklarisani newByteArray.
Nakon sto pronaĎemo handler za ovaj seter setDataByteArray, pozivamo ponovo void metod
za njega, sa newByteArray kao parametrom.
7
Bean je na ovaj nacin razmjenjen (shared ) izmedju Java programa i native dll-a.

7
Shared object-djeljenje objekta izmedju dva programa, jednog napisanog u Javi i drugog u C++.
29 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_modifyBean


(JNIEnv * env, jobject obj, jobject bean)
{
jclass beanCls = env->GetObjectClass(bean);

jmethodID setStringMid = env->GetMethodID(beanCls, "setDataString",


"(Ljava/lang/String;)V");

// set the bean member "dataString"


jstring newString = env->NewStringUTF("world");
env->CallVoidMethod(bean, setStringMid, newString );

// set the bean member "dataByteArray"


jbyteArray newByteArray = env->NewByteArray(5);
jbyte buffer[5] = {5,4,3,2,1};
env->SetByteArrayRegion( newByteArray, 0, 5, buffer);

jmethodID setByteArrayMid = env->GetMethodID(beanCls, "setDataByteArray",


"([B)V");

env->CallVoidMethod(bean, setByteArrayMid, newByteArray );


}

2.3.3.4. CrashTheJVM()
CrashTheJVM metoda pokušava da kreira double niz, koji je veći od dimenzije steka, što će
dovesti do stack overflow-a. Ovo može da izazove velike probleme u produkciji, kao za primjer
web servera, i obori JVM.

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_crashTheJvm


(JNIEnv * env, jobject)
{
//stack overflow - http://en.wikipedia.org/wiki/Stack_overflow
double x[1000000];
}

Testiranjem Main metode JniExample Java projekta, dobijamo u konzoli Eclipse-a sledece,
odnosno potvrdu da je došlo do rušenja JVM, ukoliko poziv metod jniExample.runExample4()
nije pod komentarima kao što smo naveli ranije.

30 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Starting JavaMain...
starting runExample1...
Thanks for watching this video
starting runExample2...
returned= string = this bean has been created in
Java_rs_edu_fit_se411_jni_JniExample_createAndReturnBean / byteArray =
starting runExample3...
before: string = hello / byteArray = 1 2 3
after: string = world / byteArray = 5 4 3 2 1
starting runExample4...
An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0fb11d97, pid=5700, tid=4832
#
# JRE version: 6.0_18-b07
# Java VM: Java HotSpot(TM) Client VM (16.0-b13 mixed mode, sharing windows-x86
# Problematic frame:
# C [JniExampleLibrary.dll+0x11d97]
#
# An error report file with more information is saved as:
# E:\Dev\Java\workspace\SE411\JniExample\hs_err_pid5700.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

2.3.3.5.Kompletan listing klase JniExampleLibrary.cpp


Ispod je listing kompletne klase JniExampleLibrary.cpp. U svim metodama postoji parametar
JniEnv, koji je pokazivač na Jni Enviroment (tj. JNI okruzenje).

#include "rs_edu_fit_se411_jni_JniExample.h"
#include <cstring>

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_callJavaMethod


(JNIEnv * env, jobject obj)
{
jclass jniExampleCls = env->GetObjectClass(obj);

jmethodID mid = env->GetMethodID(jniExampleCls, "printSomething", "()V");

31 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

env->CallVoidMethod(obj, mid);
}

JNIEXPORT jobject JNICALL Java_rs_edu_fit_se411_jni_JniExample_createAndReturnBean


(JNIEnv * env, jobject obj)
{
jclass beanClass = env->FindClass("codebazaar/example/Bean");
jmethodID constructorMethodId = env->GetMethodID(beanClass, "<init>", "()V");
jobject bean = env->NewObject(beanClass, constructorMethodId);

jstring newString = env->NewStringUTF("this bean has been created in


Java_codebazaar_example_JniExample_createAndReturnBean");

jmethodID setStringMid = env->GetMethodID(beanClass, "setDataString",


"(Ljava/lang/String;)V");

env->CallVoidMethod(bean, setStringMid, newString );

return bean;
}

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_modifyBean


(JNIEnv * env, jobject obj, jobject bean)
{
jclass beanCls = env->GetObjectClass(bean);

jmethodID setStringMid = env->GetMethodID(beanCls, "setDataString",


"(Ljava/lang/String;)V");

// set the bean member "dataString"


jstring newString = env->NewStringUTF("world");
env->CallVoidMethod(bean, setStringMid, newString );

// set the bean member "dataByteArray"


jbyteArray newByteArray = env->NewByteArray(5);
jbyte buffer[5] = {5,4,3,2,1};
env->SetByteArrayRegion( newByteArray, 0, 5, buffer);

jmethodID setByteArrayMid = env->GetMethodID(beanCls, "setDataByteArray",


"([B)V");

env->CallVoidMethod(bean, setByteArrayMid, newByteArray );

32 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

JNIEXPORT void JNICALL Java_rs_edu_fit_se411_jni_JniExample_crashTheJvm


(JNIEnv * env, jobject)
{
//stack overflow - http://en.wikipedia.org/wiki/Stack_overflow
double x[1000000];
}

33 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2.4. TESTIRANJE POZIVA IZMEĐU JAVA PROJEKTA I C++ DLL-A

2.4.1. KOMPAJLIRANJE DLL-A


Prije početka testiranja, potrebno je kompajlirati solution8 u Visual Studiju, na sledeći način: Build-
>Rebuild Solution opcijom iz glavnog menija. Vidimo da se kreirani dll zove
JniExampleLibrary.dll.

Slika9.Build solution Output u VS 2010

2.4.2. KONFIGURISANJE PUTANJE DO DLL-A U ECLIPSE-U


Sledeći problem je što smo naveli u Java kodu klase JniExample da želimo da učitamo dll
JniExampleLibrary, meĎutim nigdje nismo naveli putanju do tog dll-a. Nalazimo da je lokacija
dll-a E:\Dev\Cpp\SE411\JniExampleLibrary\JniExampleLibrary\Debug, i potrebno je to da
konfigurišemo kroz Eclipse na sledeći način:

Slika 10.Kreiranje nove konfiguracije u Eclipse-u, i postavljanje nove Enviroment Variable

8
Visual Studio Solution-Termin za kontejner za jedan ili više projekata u Visual Studiju, koji predstavljaju
jednu logičku cjelinu.
34 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

tj. idemo na Run->Run Configurations, i u Java_Application čvoru dodajemo novu konfiguraciju, i


na tabu Enviroment za PATH dodajemo %PATH%;<Putanja do dll-a> . TakoĎe na Main tabu
treba postaviti Main za polaznu tačku aplikacije (idemo na Search i odaberemo klasu).

2.4.3. POKRETANJE JAVA APLIKACIJE


U Main metodi potrebno je da bude zakomentarisan poziv runExample4, za prvi test, tj. da stoji
//jniExample.runExample4();
U prethodnom dijalogu, sa slike 10., potrebno je potvrditi podešavanja sa Apply pa sa Run
pokrenuti program. Dobija se sledeći rezultat:
Starting JavaMain...
starting runExample1...
Thanks for watching this
starting runExample2...
returned= string = this bean has been created in
Java_rs_edu_fit_se411_jni_JniExample_createAndReturnBean / byteArray =
starting runExample3...
before: string = hello / byteArray = 1 2 3
after: string = world / byteArray = 5 4 3 2 1

odnosno, pozivaju se prva tri metoda iz dll-a, dok je četvrti nepozvan, koji treba da izazove pucanje
JVM-a, jer je zakomentarisan metod koji u svom tijelu poziva ovaj metod dll-a.

2.4.4. PUCANJE JVM


Odkomentarišimo četvrti metod u Main klasi, tj. jniExample.runExample4() i ponovo pokrenimo
program. Dobijamo u konzoli poruku, kao iz listinga 2.3.3.4.

35 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

2.5. DEBUG JAVA KODA I C++ DLL-A

2.5.1. BREAKPOINT JAVA PROJEKTA/DEBUG


Postavimo breakpoint na liniji jniExample.runExample4() Main klase i pokrenimo Debug (Run-
>Debug As->Java_Application). Eclipse nas prebacuje u Debug perspektivu, kao na slici:

Slika 11.Debug Java koda

2.5.2. BREAKPOINT NA C++ PROJEKTU


Postavimo breakpoint na poziv četvrtog metoda u cpp klasi JniExampleLibrary, u Visual Studiju

Slika12.Povezivanje sa Javaw.exe procesom

36 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

Sem breakpointa, potrebno je i povezati projekat sa procesom javaw.exe, kao na slici 12, preko
Debug->Attach To Process.. , odabrati javaw.exe iz liste i sa Attach potvrditi.

2.5.3. DEBUG DLL-A IZ JAVA KODA


Vratimo se na Eclipse. Pokrenimo Resume (ili F8). Vidimo da se poziva debug i u Visual Studiju,
ulazimo u tijelo metode u cpp klasi,

Slika13.Debug u Visual Studiju, iz javaw.exe procesa

i obzirom da izazivamo pucanje JVM, Eclipse nas izbacuje, iz debug-a, kao na slici 14.

Slika14.Kraj Debug-a u Eclipse-u

2.5.4. SPRJEČAVANJE PUCANJA JVM U PRIMJERU


Napisani kod u dll-u je lako ispraviti, dovoljno je staviti za dimenziju niza broj koji neće izazvati
stack-overflow, npr. 100. Pokrenimo ponovo debug. U ovom slučaju neće doći do pucanja JVM.

37 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

3.ZAKLJUČAK

Java okruženje i jezik je siguran i efikasan način za razvoj aplikacija. Pa ipak, ponekad je za
aplikaciju potrebna odreĎena funkcionalnost, koju Java, zbog svoje težnje da bude
multiplatfomska, nije moguće postići, ili bar ne upotrebom isključivo Java koda.
Zbog toga se kao logično rješenje nameće potreba za integracijom Jave i programa koji koriste
mogućnosti hardvera za koga su napisane, ili operativnog sistema. C/C++ je primjer takvog
jezika, koji se prevodi u mašinski kod.
Sledeći razlog integracije ova dva jezika je što želimo da komuniciramo sa već napisanim
softverom, kakav je moguće napisati i upotrebom Jave, ali ne želimo kod pisati iznova,
pogotovu ako se radi o velikim sistemima, jer bi to zahtjevalo velike resurse, ali i vrijeme koga
najčešće nemamo.
Zavisno od zahtjevane funkcionalnosti Java koda, pa i od izbora samog programera, ovu
integraciju je moguće postići na više načina, tj. upotrebom različitih API-ja, nekomercijalnih, i
komercijalnih. Ovakva integracija, uz niz prednosti, i obezbjeĎivanja funkcionalnosti, koje inače
ne bi bile moguće samo upotrebom Jave, donosi i niz problema. Jedan od njih je što loše
napisan C++ kod može dovesti do pucanja Java virtuelne masine, što je posebno opasno kod
izvršavanja ovakvog koda na web serverima.
Nijedan od predstavljenih načina integracije nije savršen, ali uz niz problema o kojima moramo
voditi računa (kao što je nepostojanje Garbage Collector-a u okviru C++, zbog čega moramo
eksplicitno uništavati objekte, ili gubitka na portabilnosti ovakvog koda) donosi i niz prednosti i
obezbjeĎuje funkcionalnosti koji upotrebom samo Jave i Javinih biblioteka ne bi bile moguće.

38 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

4. SUGESTIJE ZA NASTAVAK RADA

U ovom radu su predstavljeni različiti pristupi integraciji Java i C/C++ koda. Dat je kratak pregled
najčešće korišćenih API-ja, kao i preporuke koji odabrati u konkretnoj situaciji. Nijedan od njih
nije savršen, i ima svoje nedostatke, o kojima posebno treba voditi računa.
Situacija se na ovom polju, usavršavanjem Jave, odnosno JVM, stalno mijenja, i bajt kod po brzini
dostiže sličan napisan u jezicima koji se prevode u mašinski kod. Tako jedan od aspekata ove
integracije gubi na značaju.
Pisano je o načinu na koji funkcioniše JNI, mapiranjima izmedju C/C++ i Java tipova podataka, kao
i problemima koji nastaju upotrebom ovog API-ja. Dalje je moguće detaljnije pisati o:
 održavanju Java reference JNIEnv u C/C++ kodu
 primitivnom tipovima, nizovima, stringovima, i njihovoj upotrebi
 kreiranju i uništavanju objekata
 raspodjeli memorije
 interfejsima
 izuzecima
 i drugim temama, vezanim za ovaj kompleksni, robustni API.
I drugi API-ji su predstavljeni na način da čitalac ima tek uvid o njima. Za njihovo bolje
razumjevanje, potrebno je detaljnije istražiti njihovu namjenu, polja primjene, probleme u
upotrebi. Kao polaznu tačku, čitalac može koristi literaturu korištenu u ovom radu.

39 | S t r a n a
[SE411-INTEGRATIVNO PROGRAMIRANJE U DVA PROGRAMSKA JEZIKA C++/JAVA]
December 16, 2010

5. LITERATURA

http://www.ensta.fr/~diam/java/online/io/javazine.html
http://www.javaworld.com/jw-12-2000/jw-1229-traps.html?page=1
http://stackoverflow.com/questions/782827/jni-vs-runtime-exec
http://gcc.gnu.org/onlinedocs/gcj/Basic-concepts.html#Basic-concepts
http://en.wikipedia.org/wiki/Java_Native_Interface
http://www.ibm.com/developerworks/java/library/j-jni/index.html
http://download.oracle.com/javase/1.4.2/docs/guide/jni/spec/jniTOC.html
http://www.drdobbs.com/184401335
http://gcc.gnu.org/java/papers/native++.html
http://codebazaar.blogspot.com/2010/08/package-codebazaar.html
http://download.oracle.com/javase/1.4.2/docs/guide/awt/1.3/AWT_Native_Interface.html
http://java.sun.com/javame/reference/docs/kni/html/02-Goals.html
http://en.wikipedia.org/wiki/GNU_Classpath
http://en.wikipedia.org/wiki/Compiled_Native_Interface
http://en.wikipedia.org/wiki/SWIG
http://www.swig.org/
http://www.swig.org/Doc1.3/Java.html
http://en.wikipedia.org/wiki/Java_Native_Access
http://stackoverflow.com/questions/3720563/access-c-shared-library-from-java-jni-jna-cni-or-swig

40 | S t r a n a

You might also like