Professional Documents
Culture Documents
Java Praktikum
Java Praktikum
Branko Milosavljevi
Milan Vidakovi
Sadraj
5
6
7
9
9
10
10
11
13
14
14
15
17
17
19
19
20
21
22
22
23
23
24
24
25
1.25 Izuzeci
1.26 Klasa Object
1.27 Klasa String
1.28 Primeri nekih klasa iz standardne biblioteke
1.28.1 Klasa java.util.Vector
1.28.2 Klasa java.util.Hashtable
1.28.3 Klasa java.util.StringTokenizer
25
28
28
29
30
30
31
31
31
33
50
51
53
57
57
58
59
60
61
63
66
67
67
68
69
69
70
70
74
102
102
102
102
103
104
105
108
109
111
115
115
117
117
118
119
120
121
10.3. CORBA
10.3.1. Osnovne odrednice
10.3.2. IDL
10.3.3. CORBA Naming Service
10.3.4. Proces pisanja CORBA programa
10.3.5. CORBA izuzeci
10.3.6. Pozivi unatrag
10.3.7. RMI i CORBA
124
125
126
126
127
127
129
129
129
130
131
131
134
136
137
137
138
139
140
140
147
Literatura .....................................................................................................................152
Prilozi ...........................................................................................................................154
Poglavlje 0
objektno-orijentisano programiranje: poznavanje osnovnih pojmova i koncepata (klasa, objekat, apstrakcija, nasleivanje, polimorfizam);
konkurentno programiranje: pojmovi procesa i niti; rasporeivanje procesa,
sinhronizacija procesa, nedeljive operacije;
relacione baze podataka i SQL: poznavanje relacionog modela podataka,
njegova implementacija u okviru sistema za upravljanje relacionim bazama podataka, upotreba jezika SQL za operacije nad bazom podataka;
HTML: osnovni elementi strukture HTML dokumenata, rukovanje Web
itaima;
U prvom poglavlju, Uvod u programski jezik Java, govori se o osnovnim karakteristikama Jave, kao programskog jezika, i kao platforme za izvravanje programa. Upoznaje se koncept Java virtuelne maine (JVM) i prenosivosti
prevedenog Java koda. Zatim se vri pregled osobina Jave kao programskog
jezika, i obrauju se jezike konstrukcije.
Drugo poglavlje nosi naziv Konkurentno programiranje u Javi i donosi pregled
jezikih koncepata koji omoguavaju pisanje konkurentnih programa.
Tree poglavlje, GUI aplikacije i JavaBeans, predstavlja saeti prikaz pisanja
aplikacija i apleta sa grafikim korisnikim interfejsom. Definie se struktura
ovakvih aplikacija, nain reagovanja na dogaaje koje izaziva korisnik i navode
primeri korienja brojnih komponenti za izgradnju korisnikog interfejsa.
etvrto poglavlje, Mreno programiranje u Javi, definie pojmove koji se koriste
prilikom pisanja programa koji komuniciraju preko mree, a zatim opisuje
elemente jezika koji se koriste za pisanje ovakvih programa. Podrazumeva se
rad preko TCP/IP mree.
Peto poglavlje sadri vebu, iji je cilj konstrukcija mrene klijent/server
aplikacije za chat preko TCP/IP mree. Konstrukcija ovakvog sistema obuhvata
sve prethodno obraene teme.
U estom poglavlju Rad sa bazama podataka JDBC dat je uvod u metode pristupa i korienja relacionih baza podataka iz Java programa. Podrazumeva se
korienje sistema za upravljanje relacionim bazama podataka sa kojima se
komunicira preko jezika SQL.
Sedmo poglavlje, Uvod u vieslojne klijent/server sisteme, definie okvire u kojima
se nalazi materija izloena u narednim poglavljima.
Osmo poglavlje Dinamiko generisanje HTML-a i servleti prikazuje servlete,
osnovnu Java tehnologiju za dinamiko generisanje Web sadraja i izgradnju
Web sajtova.
U narednom poglavlju, Java Server Pages, predstavljena je tehnologija za pisanje
dinamikih Web stranica koja omoguava razdvajanje zadataka Web dizajnera i
programera, pojednostavljujui tako razvoj Web-orijentisanih informacionih
sistema.
Deseto poglavlje, Tehnologije distribuiranih objekata, donosi saet prikaz tehnologija namenjenih za pisanje distribuiranih objektno-orijentisanih aplikacija
dostupnih iz programskog jezika Java.
Poslednje poglavlje sadri vebu iji je cilj konstrukcija Web aplikacije za
elektronsko poslovanje. Zadatak je napisati softver za Web sajt koji omoguava
kupovinu putem Web-a.
Poglavlje 1
Veliina
1-bit
16-bit
8-bit
16-bit
32-bit
64-bit
Minimum
Unicode 0
-128
-215
-231
-263
Maksimum
Unicode 216-1
+127
+215-1
+231-1
+263-1
Primitivni tip
float
double
void
Veliina
32-bit
64-bit
-
Minimum
IEEE 754
IEEE 754
Tabela 1.1. Primitivni tipovi
Maksimum
IEEE 754
IEEE 754
-
void upali() {
radi = true;
}
void ugasi() {
radi = false;
}
(Plavom bojom su navedene kljune rei jezika). Klasa ima naziv Automobil,
definie jedan atribut koji se zove radi i logikog je tipa (boolean), i definie dve
metode koje se mogu pozvati nad objektima te klase, metode upali i ugasi.
Kreiranje objekata koji predstavljaju instance (primerke) ove klase moe se
obaviti na sledei nain:
Automobil a = new Automobil();
Automobil b = new Automobil();
Time su kreirana dva objekta klase Automobil, koji su nazvani a i b. Atributu radi
objekta a moe se pristupiti pomou:
a.radi
Ovo do sada reeno izuzetno podsea na C++. Neke od osobina Jave koje je
bitno razlikuju u odnosu na C++ su:
Kako Java ne doputa postojanje bilo ega to bi postojalo izvan neke klase,
postavlja se pitanje odakle poinje izvravanje Java programa. C i C++ koriste
funkciju main kao osnovnu funkciju od koje poinje izvravanje programa. Java
takoe koristi funkciju main, samo to i ta funkcija mora biti metoda neke klase
(u C++ terminologiji bi se reklo funkcija lanica). Izgled jedne klase koja
sadri metodu main, i predstavlja primer jednog izvrivog Java programa dat je
u sledeem primeru:
class Hello {
public static void main(String args[]) {
System.out.println(Hello world!);
}
}
(Trenutno nije bitno zato metoda main mora biti definisana kao public
static void, ali mora biti tako.) Kompletan tekst ove klase smeten je u
datoteku Hello.java. Treba obratiti panju na naziv ove datoteke: njena
ekstenzija je obavezno .java, a ime mora biti jednako imenu klase, ukljuujui i
razliku izmeu velikih i malih slova. Standardna preporuka je da se svaka klasa
programa smeta u posebnu datoteku. Naziv datoteke mora odgovarati nazivu
klase na prethodno opisani nain. Iako e neki prevodioci dopustiti smetanje
teksta vie klasa u isti fajl, ta praksa se ne preporuuje. Dakle, svakoj Java klasi
odgovara jedan fajl sa identinim nazivom i ekstenzijom .java.
Nema nikakve prepreke da vie klasa koje ine program poseduju metodu
main. Odakle e se poeti sa izvravanjem programa? To se odreuje prilikom
pokretanja programa, tako to se navodi ime one klase iju metodu main elimo
da pokrenemo.
objekat klase
Automobil
heap
stek
objekat klase
Automobil
objekat klase
Automobil
b
a
heap
stek
objekat klase
Automobil
objekat klase
Automobil
b
a
heap
stek
1.7 Operatori
Operatori koji slue za gradnju Java izraza su operatori koji su, praktino,
preuzeti iz jezika C++. Moemo ih grupisati u nekoliko grupa:
ili, jo opasnije
if (a = 1)
if ... else
switch
for
while
do ... while
break
continue
je deklaracija reference varA koja ukazuje na objekat klase A, pri emu se odmah
vri i inicijalizacija ove reference na novokreirani objekat. U trenutku kada se
izvri ovaj red (zapravo kreiranje objekta pomou new A()), na konzoli e se
ispisati
konstruktor
U pitanju je metoda koja u okviru svog tela vri modifikaciju svog parametra a
preko metode upali. (Koristi se klasa Automobil definisana u prethodnim
primerima). U sluaju da se ova metoda pozove u sledeem segmentu koda:
Automobil x = new Automobil();
11
x.radi = false;
test(x);
// da li je atribut radi ovde true ili false?
vrednost atributa radi objekta x bie true. Slika 1.4 ilustruje ta se zapravo
desilo: na steku je kreirana referenca x na objekat klase Automobil. Zatim je
vrednost atributa radi ovog objekta postavljena na false (slika a). Nakon toga
pozvana je metoda test, sa referencom x kao parametrom. Parametri metoda se,
slino kao i u drugim programskim jezicima, smetaju na stek prilikom poziva
metode (ovde je nebitno u kom redosledu). Tako je i referenca x iskopirana na
stek jo jednom (slika b). U okviru tela metode test ova druga kopija reference x
se koristi kao parametar metode i preko nje se pristupa istom onom objektu na
koji ukazuje i originalna referenca x. Pristup objektu se u ovom sluaju svodi na
promenu vrednosti atributa radi na true (slika c). Kod vraanja iz metode
nazad, sa steka se uklanjaju parametri korieni prilikom poziva metode. Tako
se sa steka uklanja druga kopija reference x i ostaje samo originalna referenca.
Kada preko nje pristupimo atributu radi, videemo da je on promenio vrednost
(slika d).
radi: false
radi: false
x
x
x
a)
b)
radi: true
radi: true
x
x
x
c)
d)
12
tipa se smetaju na stek (trenutno stanje ilustruje slika a). Zatim se poziva
metoda test sa parametrom a; parametar a se smeta na stek (zapravo, njegova
vrednost se kopira jo jednom slika b). U okviru metode vrednost parametra
se menja u 1, pri emu se menja druga kopija na steku (slika c). Nakon povratka
iz metode, parametar se uklanja sa steka i na steku ostaje originalna vrednost
promenljive a koja nije menjana (slika d).
a=0
a=0
a=0
a)
b)
a=1
a=0
a=0
c)
d)
A {
metoda() { ... }
metoda(int i) { ... }
metoda(String s) { ... }
Kljuna re final ima drugo znaenje kod metoda: oznaava metode koje se ne
mogu redefinisati prilikom nasleivanja date klase. O nasleivanju e vie biti
rei u odeljku 1.18, a o redefinsanju metoda u odeljku 1.20. Primer jedne final
metode glasi:
final int metoda(int i) { ... }
13
Statike metode su metode koje ne mogu biti pozvane nad objektimainstancama klase, ve nad klasom samom. U tom smislu, nije mogue pisati
a.metoda();
nego samo
StaticTest.metoda();
pri emu se poziva metoda println objekta out koji je statiki atribut klase System
(out zapravo predstavlja standardni izlaz, slino kao stdout u jeziku C).
1.14 Nizovi
Nizovi se u Javi definiu vrlo slino kao u jeziku C++. Na primer, niz iji su
elementi tipa int se definie na sledei nain:
int[] a;
ili
int a[];
Ovim je samo definisana referenca na niz; niz se nakon toga mora kreirati na
nain slian kreiranju objekata. Sledi primer gde se alocira niz od pet int
elemenata:
a = new int[5];
14
Prvi element niza ima indeks nula. Postoji i nain da se niz definie, alocira
memorija za njega i odmah inicijalizuje, kao u sledeem primeru:
int[] a = { 1, 2, 3, 4, 5 };
Treba voditi rauna o tome da se prilikom definicije niza referenca na niz uva
na steku, dok se elementi niza uvaju na heap-u, slino kao i objekti. Slika 1.6
ilustruje ovu situaciju za niz definisan u prethodnom primeru.
heap
parking
heap
(Svaki niz ima definisan atribut length koji predstavlja duinu alociranog niza).
Sada e stanje u memoriji izgledati kao na slici 1.8.
parking
a1
a2
a3
a4
a5
heap
e kreirati niz od dva elementa koji su reference na nizove od tri elementa tipa
int. Stanje u memoriji e nakon kreiranja ovakvog niza izgledati kao na slici
1.9.
4
a
heap
Stanje u memoriji nakon kreiranja ovakvog niza izgledae kao na slici 1.10.
a4
parking
a2
a1
a3
a5
a6
heap
16
17
Dakle, ponovo je vano sa kog mesta se poziva Java interpreter: to mora biti
korenski direktorijum aplikacije. Svaka prevedena klasa se u okviru aplikacije
vidi u okviru paketa ija je putanja jednaka relativnoj putanji do odgovarajueg
direktorijuma. Separator naziva paketa je, kao to je ve reeno, taka, a ne kosa
crta ili obrnuta kosa crta.
Programski jezik Java stie sa velikim brojem klasa grupisanim u pakete. Te
klase su dostupne kao i klase koje sami piemo (ak se moe dobiti i njihov
izvorni kod). Recimo klasa Vector koja se nalazi u paketu java.util je u
programima dostupna kao java.util.Vector. Kako bi svako pominjanje ove
klase u tekstu programa zahtevalo navoenje pune putanje do nje (odnosno
navoenje odgovarajueg paketa), to bi program uinilo manje itljivim. Zato je
mogue na poetku teksta klase deklarisati da se koristi ta-i-ta klasa koja se
nalazi u tom-i-tom paketu. Na primer:
package paket1;
import java.util.Vector;
class Automobil { ... }
// nije dozvoljeno!
18
1.16.2 CLASSPATH
S obzirom na do sada izloeno, korienje klase Vector iz paketa java.util bi
znailo da se odgovarajue stablo direktorijuma java\util\... koje sadri
kompajlirane klase mora kopirati unutar strukture direktorijuma svake
aplikacije koju piemo. Time se bespotrebno zauzima prostor i komplikuje
odravanje softvera. Zato postoji nain da se paketi sa klasama koji se koriste iz
vie aplikacija uvaju na jednom mestu, a sve aplikacije e pomou odgovarajueg mehanizma te klase videti kao da je struktura direktorijuma iskopirana u
okviru svake aplikacije. U pitanju je mehanizam slian korienju PATH promenljive okruenja (environment variable).
Java interpreter za ovu svrhu koristi promenljivu okruenja koja se naziva
CLASSPATH. Ona sadri listu direktorijuma u kojima treba traiti klase koje se
koriste. Na primer, ukoliko je cela java\... hijerarhija paketa smetena u
direktorijum C:\java\lib, vrednost CLASSPATH promenljive bi mogla da glasi:
CLASSPATH=C:\java\lib
tada i na program moemo pokrenuti sa bilo kog mesta u okviru fajl sistema,
jer e klase biti vidljive preko CLASSPATH-a. Na primer, komanda:
e pokrenuti na program
D:\temp\korenski paket.
iako
se
ne
nalazimo
direktorijumu
19
20
CLASSPATH=.;C:\jdk1.3\jre\lib\rt.jar
21
viedimenzionalnih nizova. */
public static void main(String[] args) {...}
/* Sadrzaj matrice */
double[][] data;
/* Dimenzije matrice */
int n, m;
1.18 Nasleivanje
Nasleivanje, kao jedan od osnovnih koncepata objektno-orijentisanog programiranja, postoji i u Javi. Kada jedna klasa nasleuje drugu, potrebno je to
naglasiti u okviru teksta klase klazulom extends kao u sledeem primeru, gde
klasa BorbeniAvion nasleuje klasu Avion:
class Avion {
Krilo levo, desno;
void poleti() { ... }
void sleti() { ... }
}
class BorbeniAvion extends Avion {
Top top;
Bomba[] bombe;
void poleti() { ... }
void pucaj() { ... }
}
programu
22
na konzoli e se ispisati:
metoda1
metoda1
metoda2
metoda2
klase
klase
klase
klase
A
B
A
A
23
nije doputen.
1.22 Interfejsi
Interfejsi su poseban koncept u Javi: nisu u pitanju klase, ali interfejsi mogu da
sadre deklaracije apstraktnih metoda, konstanti i statikih atributa. Sledi
primer:
interface Instrument {
void sviraj();
void nastimaj();
}
Klasa Unutrasnja je, u principu, vidljiva samo unutar klase Spoljasnja, mada se to
moe promeniti modifikatorima pristupa na uobiajen nain. Instanca
unutranje klase se moe kreirati i izvan nje, ali samo preko instance spoljanje
klase, kao u sledeem primeru:
Spoljasnja s = new Spoljasnja();
Spoljasnja.Unutrasnja u = s.new Unutrasnja();
24
1.24 Polimorfizam
Polimorfizam je koncept koji omoguava objektima da ispolje razliito
ponaanje, zavisno od njihove klase, bez obzira to se oni koriste kao instance
nekog zajednikog roditelja. Posmatrajmo sledee tri klase:
abstract class Instrument {
abstract void sviraj();
}
class Violina extends Instrument {
void sviraj() { ... }
}
class Klarinet extends Instrument {
void sviraj() { ... }
}
Dakle, primer definie tri klase: klasa Instrument je apstraktna klasa (njena
metoda sviraj je apstraktna), a klase Violina i Klarinet nasleuju klasu Instrument
i, naravno, implementiraju (zapravo, redefiniu) apstraktnu metodu. Posmatrajmo sada klasu Muzicar:
class Muzicar {
void sviraj(Instrument i) {
i.sviraj();
}
}
Vidimo da klasa Muzicar ima metodu sviraj koja kao parametar ima instancu
klase Instrument; sa druge strane, znamo da klasa Instrument ne moe imati
instance, jer je apstraktna. Ova metoda e ipak biti upotrebljiva, jer se njoj kao
parametar moe proslediti instanca neke klase koja nasleuje klasu Instrument
u ovom sluaju instance klasa Violina i Klarinet. Iskaz
Muzicar m = new Muzicar();
m.sviraj(new Klarinet());
e izazvati pozivanje metode sviraj klase Violina po istom principu. Dakle, poziv
metode sviraj klase Muzicar e imati razliite efekte zavisno od toga koji objekat
prosledimo kao parametar. Odreivanje koja metoda e se pozvati se obavlja u
toku izvravanja programa.
U primeru se vidi da ovo specijalno ponaanje metoda nije niim naglaeno u
tekstu programa. Ovakav efekat se u jeziku C++ postizao korienjem tzv.
virtuelnih funkcija lanica, a u Javi je ovo podrazumevano (i jedino mogue)
ponaanje. Dakle, moemo rei, u terminologiji jezika C++, da su sve metode u
Javi virtuelne, pa se ta osobina ne mora naglaavati posebno u programu.
1.25 Izuzeci
Izuzeci su mehanizam za kontrolu toka programa koji se koristi za obradu
greaka nastalih u toku izvravanja programa. Segment programskog koda za
25
Izuzetak moe biti, na primer, deljenje nulom, pristup elementu niza koji je
izvan granice niza, itd. Ukoliko se prilikom izvravanja koda koji se nalazi u try
bloku desi izuzetak, tok izvravanja programa se automatski prebacuje na
poetak catch bloka. Nakon izvravanja koda u catch bloku, program dalje
nastavlja rad.
U okviru catch bloka informacije o samom izuzetku koji se dogodio su dostupne
preko objekta klase Exception ili neke njene naslednice. U primeru je to objekat
ex.
Razliite vrste izuzetaka su predstavljene razliitim exception klasama, na
primer: svi izuzeci prilikom izvravanja aritmetikih operacija (deljenje nulom,
overflow, itd.) su predstavljene klasom ArithmeticException, pristup elementu iji
je indeks izvan granice niza je predstavljen klasom ArrayIndexOutOfBoundsException, itd. Klasa Exception je zajedniki predak svim exception klasama.
Jedan try blok moe imati vie sebi pridruenih catch blokova, kao u sledeem
primeru:
try {
// kod koji moe da izazove
// izuzetak
}
catch (ArithmeticException ex) {
System.out.println("Deljenje nulom");
}
catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("Pristup van granica niza");
}
catch (Exception ex) {
System.out.println("Svi ostali izuzeci");
}
finally {
// kod koji se izvrava u svakom sluaju
}
26
Programski kod koji sadri ovakvu throw naredbu mora biti smeten unutar try
bloka koji hvata izuzetak MojException na to e nas naterati kompajler. Dakle,
ovo bi moglo da izgleda na sledei nain:
try {
if (errorCheck())
throw new MojException("Houston, we have a problem.");
} catch (MojException ex) {
System.out.println("Exception: " + ex);
}
Sada poziv ovakve metode mora biti u odgovarajuem try bloku, ili metoda u
kojoj sadri ovaj poziv mora isto biti oznaena da moe da izazove izuzetak.
Dakle:
public void m1() {
try {
metoda();
} catch (MojException ex) {
System.out.println("Exception: " + ex);
}
}
27
ili:
public void m1() throws MojException {
metoda();
}
+ "prezime"
ili
x + "prezime"
ili x + y
i njen poziv
handleMessage(new Automobil());
nee izazvati promenu objekta koji je prosleen kao parametar, jer e se u telu
metode, prilikom konkatenacije, generisati novi String objekat koji e biti
dodeljen lokalnoj kopiji reference message. Po povratku iz metode unitava se
promenjena lokalna kopija reference, i ostaje samo originalna referenca koja i
dalje ukazuje na stari String objekat.
29
}
System.out.println(ht);
@return
@throws
@see
@author
Podaci o autoru.
@author author-info
@version
Opis verzije.
@version version-info
@since
@deprecated
Oznaava zastarelu mogunost koja moe biti naputena u sledeim verzijama. Kompajler e generisati
upozorenje ako se koriste ovakve metode ili atributi.
Primer jedne Java klase koja sadri odgovarajue javadoc komentare dat je u
nastavku.
/** Klasa namenjena za rad sa matricama
* @author Branko Milosavljevi
* @version 1.0
*/
public class Matrix {
/** Konstruktor
* @param n Broj redova matrice
* @param m Broj kolona matrice
* @throws MathException U sluaju da je neka od
*
dimenzija manja od 1
*/
public Matrix(int n, int m) throws MathException { ... }
/** Postavlja sadraj matrice.
* @param x Nova vrednost matrice
* @throws MathException U sluaju da je neka od
32
*
dimenzija manja od 1, ili je vrednost parametra
*
<code>null</code>
*/
public void setData(double[][] x) throws MathException { ... }
/** Vraa sadraj matrice.
* @return Tekua vrednost matrice
*/
public double[][] getData() { ... }
/** Mnoi sadraj matrice objekta koji je pozvan (this) sa
* sadrajem matrice b (objekta koji je prosleen kao
* parametar). Rezultat mnoenja smeta u novi objekat koga
* vraa kao rezultat metode.
* @param b Druga matrica u mnoenju
* @return Rezultat mnoenja
* @throws MathException U sluaju neispravnih dimenzija matrica
*/
public Matrix multiply(Matrix b) throws MathException { ... }
/** Mnoi sadraj dve date matrice i rezultat mnoenja
* vraa kao rezultat metode. Obratiti panju da je ovo
* statika metoda!
* @param a Prvi inilac poizvoda
* @param b Drugi inilac proizvoda
* @return Rezultat mnoenja
* @throws MathException U sluaju neispravnih dimenzija matrica
*/
public static Matrix multiply(Matrix a, Matrix b)
throws MathException { ... }
/** Mnoi sadraj matrice objekta koji je pozvan (this) sa
* sadrajem matrice b (objekta koji je prosleen kao
* parametar). Rezultat mnoenja se smeta u matricu objekta
* koji je pozvan. Metoda ne vraa nikakav rezultat!
* @param b Drugi inilac proizvoda
* @throws MathException U sluaju neispravnih dimenzija matrica
*/
public void multiply2(Matrix b) throws MathException { ... }
/** Vraa string reprezentaciju objekta.
* @return String reprezentacija objekta
*/
public String toString() { ... }
/** Sadraj matrice */
private double[][] data;
33
34
Poglavlje 2
35
Od ove take nadalje, na program se sastoji iz dve niti: osnovne niti programa
koja poinje svoje izvravanje od metode main, i novostvorene niti koja je
opisana u metodi run klase MojThread. Slika 2.1 ilustruje ovu situaciju.
osnovna nit
mt
Ukoliko je programski kod metode run jednak u oba sluaja, obe varijante su
funkcionalno ekvivalentne.
37
Na slici vidimo da je prva ispisana poruka zapravo poruka koju ispisuje metoda
main kada zavrava sa radom. To znai da je u ovom sluaju, prilikom
pokretanja programa, osnovna nit programa stigla da izvri celokupan svoj
programski kod pre nego to su druge niti dobile priliku da zauzmu procesor.
Samim tim, ovo je ilustracija sluaja gde zavravanje osnovne niti programa ne
predstavlja i zavravanje celog programa: postoji jo deset non-daemon niti koje
nisu zavrile svoj rad. Ova situacija bi se grafiki mogla predstaviti kao na slici
2.3.
osnovna nit
new PrviThread(0).start();
new PrviThread(1).start();
new PrviThread(2).start();
...
new PrviThread(9).start();
x
x
x
x
x
Slika 2.3. Grafika predstava izvravanja programa ThreadTest
38
nit B
sync {
2) locked?
1) lock
obj
sync {
3) yes
4) unlock
5) lock
wait
}
6) unlock
39
nit B
sync {
wait();
obj
sync {
waiting
for
notify
notify
notify();
waiting
for
lock
}
}
Ove tri metode mogu biti pozvane samo unutar synchronized bloka i to nad
objektom nad kojim se vri sinhronizacija.
prazniti bafer (osim ako nije prazan, tada mora da eka). U naem primeru nit
proizvoa je implementirana klasom Producer, a nit potroa klasom
Consumer.
public class Consumer extends Thread {
public Consumer(Buffer buffer, int count) {
this.buffer = buffer;
this.count = count;
}
public void run() {
for (int i = 0; i < count; i++)
buffer.read();
}
private Buffer buffer;
private int count;
}
public class Producer extends Thread {
public Producer(Buffer buffer, int count) {
this.buffer = buffer;
this.count = count;
}
public void run() {
for (int i = 0; i < count; i++)
buffer.write((int)Math.round(Math.random() * 100));
}
private Buffer buffer;
private int count;
}
41
}
/** Upisuje novu vrednost u bafer.
* @param value Nova vrednost koja se upisuje
*/
public synchronized void write(int value) {
if (isFull()) {
System.out.println("Waiting to write...");
try { wait(); }
catch (Exception ex) { ex.printStackTrace(); }
}
data[writePos] = value;
if (++writePos == size)
writePos = 0;
notify();
System.out.println("Written: "+value);
}
/** ita narednu vrednost iz bafera.
* @return Proitana vrednost
*/
public synchronized int read() {
if (isEmpty()) {
System.out.println("Waiting to read...");
try { wait(); }
catch (Exception ex) { ex.printStackTrace(); }
}
int retVal = data[readPos];
if (++readPos == size)
readPos = 0;
System.out.println("Read: "+retVal);
notify();
return retVal;
}
/** Ispituje da li je bafer prazan.
* @return Vraa <code>true</code> ako je bafer prazan
*/
public synchronized boolean isEmpty() {
return readPos == writePos;
}
/** Ispituje da li je bafer pun.
* @return Vraca <code>true</code> ako je bafer pun
*/
public synchronized boolean isFull() {
return readPos == (writePos + 1) % size;
}
/** Veliina krunog bafera */
private int size;
/** Sadraj krunog bafera */
private int[] data;
/** Naredna lokacija za itanje */
private int readPos;
/** Naredna lokacija za pisanje */
42
writePos readPos
writePos readPos
b)
writePos
readPos
readPos
writePos
c)
readPos
writePos
Slika 2.6. a) Sluajevi kada je bafer prazan; b) sluajevi kada je bafer pun;
c) sluajevi kada bafer nije ni prazan ni pun
43
44
Poglavlje 3
45
tivnom sistemu). Kasnije su se pojavili look-and-feel dodaci sa Macintosh izgledom, itd. Promena izgleda aplikacije moe da se obavi ak i za vreme
izvravanja programa.
Iako se Swing biblioteka moe koristiti i sa Java verzijom 1.1 (uz dodavanje
biblioteke u CLASSPATH), sve mogunosti biblioteke su dostupne tek od
verzije 1.2. Od verzije 1.2 ova biblioteka je proglaena za standard za razvoj
korisnikog interfejsa u Java aplikacijama, dok je AWT zadran zbog
kompatibilnosti sa starijim programima. Swing je postao sastavni deo vee
biblioteke nazvane Java Foundation Classes (JFC).
U ovom praktikumu bie rei iskljuivo o Swing komponentama.
Sledi primer jedne elementarne GUI aplikacije koja ima main metodu i otvara
prozor.
public class MyApp {
public static void main(String[] args) {
MainFrame mf = new MainFrame();
mf.setVisible(true);
}
}
import javax.swing.*;
public class MainFrame extends JFrame {
public MainFrame() {
setSize(300, 200);
setTitle("My First GUI App");
}
}
Aplikacija se sastoji iz dve klase: klasa MyApp samo sadri main metodu. U
okviru main metode kreira se objekat klase MainFrame (to predstavlja inicijalizaciju glavnog prozora aplikacije) i zatim se taj prozor prikae na ekranu
(poziv metode setVisible). Klasa MainFrame nasleuje klasu JFrame, to je
standardan nain za definisanje novih prozora. Klasa JFrame je deo Swing
biblioteke smetene u paket javax.swing. Komponente Swing korisnikog
interfejsa po pravilu poinju velikim slovom J. U okviru konstruktora klase
MainFrame se postavlja veliina prozora u pikselima (setSize) i naslov prozora
(setTitle).
47
import javax.swing.*;
public class MainFrame extends JFrame {
public MainFrame() {
setSize(300, 200);
setTitle("My Second GUI App");
// dodajemo komponente na formu:
getContentPane().add(bOK, BorderLayout.NORTH);
getContentPane().add(bCancel, BorderLayout.SOUTH);
}
// elementi na formi su najee privatni atributi klase
private JButton bOK = new JButton("OK");
private JButton bCancel = new JButton("Cancel");
}
CENTER
SOUTH
48
EAST
WEST
NORTH
a)
b)
49
java.awt.*;
javax.swing.*;
com.borland.jbcl.layout.*;
class MainFrame extends JFrame {
public MainFrame() {
setSize(300, 200);
setTitle("My Second GUI App");
// biramo layout manager:
getContentPane().setLayout(new XYLayout());
// dodajemo komponente na formu:
getContentPane().add(bOK,
new XYConstraints(10, 10, 100, -1));
getContentPane().add(bCancel,
new XYConstraints(10, 50, 100, -1));
}
private JButton bOK = new JButton("OK");
private JButton bCancel = new JButton("Cancel");
}
Slika 3.4 predstavlja izgled ovakve aplikacije u dve varijante veliine prozora.
b)
a)
Ovakav prozor sadri dva dugmeta koja e, na klik miem, reagovati na isti
nain koriste isti Listener zatvaranjem aplikacije. (Ovo morate probati praktino, slika ne pomae puno).
3.7.2 Oslukivai kao unutranje klase
Prozori esto znaju biti pretrpani komponentama koje, sa svoje strane, obrauju
vie vrsta dogaaja. Rezultat moe biti jedna prozorska klasa koja sadri par
desetina komponenti, i nekoliko desetina Listener klasa. Definisati pedesetak
Listener klasa samo za jedan prozor moe uiniti program nepreglednim. Zato
se Listener klase najee definiu kao unutranje klase u okviru prozorske
klase. Posmatrajmo sledei segment programskog koda:
ActionListener a = new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.exit(0);
}
});
52
Opis
povezuje vie radio button-a da rade zajedno; nije vidljiva komponenta
dugme
check box
combo box
dijalog (prozor kome se ne moe menjati veliina)
prozor
labela
list box
meni
linija menija
stavka menija
prozor koji ispisuje krau poruku (message box)
komponenta koja je kontejner za druge komponente
radio button
kartice (tabs); pojedine kartice se na ovu komponentu dodaju kao JPanel-i
vielinijsko polje za unos teksta (memo)
jednolinijsko polje za unos teksta
Tabela 3.1. Najee koriene GUI komponente
Odgovarajui listener
ActionListener
AdjustmentListener
ComponentListener
ContainerListener
FocusListener
KeyListener
MouseListener
WindowListener
ItemListener
TextListener
Tabela 3.2. Tipovi dogaaja i odgovarajui listener-i
53
54
55
cp.add(l);
56
3.9 Apleti
3.9.1 Pojam apleta
Apleti su posebna vrsta Java programa koji su namenjeni za ugraivanje u
HTML stranice. U okviru stranice aplet dobija na raspolaganje odreenu
pravougaonu povrinu ije su dimenzije date u pikselima. U tom smislu, aplet
se u HTML stranicu ugrauje na slian nain kao i slika.
Kada Web ita pristupi stranici koja sadri aplet, automatski e preuzeti i
programski kod apleta (prevedene Java klase), pokrenuti Java virtuelnu mainu
i poeti izvravanje apleta.
Aplet je Java program koji na raspolaganju ima gotovo sve mogunosti
klasinih Java aplikacija, izuzev dva bitna ogranienja, uvedena iz bezbednosnih razloga:
Aplet je zapravo klasa koja nasleuje klasu Applet (za AWT) ili JApplet (za
Swing). Pisanje apleta svodi se na nasleivanje klase JApplet i implementiranje
odgovarajuih metoda. Neke od najvanijih metoda pobrojane su ovde:
init: poziva je Web ita prilikom uitavanja apleta u JVM Web itaa
destroy: poziva je Web ita prilikom uklanjanja apleta iz JVM Web itaa;
obino se koristi za oslobaanje zauzetih resursa (npr. zaustavljanje niti
ili zatvaranje mrene konekcije)
start: poziva je Web ita kada hoe da naznai da aplet treba da pone sa
svojim izvravanjem
stop: analogno prethodnom, poziva je Web ita kada aplet treba da
prekine sa svojim izvravanjem
paint: poziva je Web ita kada je potrebno da aplet iscrta svoj sadraj
57
Aplet klasa se zove AppletTest, nasleuje klasu JApplet i redefinie metodu init.
U okviru init metode vri se inicijalizacija apleta: u ovom sluaju to se svodi na
postavljanje jedne labele na povrinu apleta.
Aplet nema metodu main, tako da ga ne moemo pokrenuti na do sada poznat
nain iz komandne linije. Potrebno je ovakav aplet ugraditi u HTML stranicu
u okviru koje e biti prikazan. Sledi primer ovakve HTML stranice.
<html>
<head>
<title>Test stranica sa apletom</title>
</head>
<body>
<applet code = "AppletTest" width = 100 height = 50>
</applet>
</body>
</html>
58
import javax.swing.*;
public class JButtonTest extends JApplet {
JButton b;
public void init() {
b = new JButton("Pritisni me");
Container cp = getContentPane();
cp.add(b);
}
}
java.awt.*;
java.awt.event.*;
javax.swing.*;
class AppletApplicationTest extends JApplet {
60
62
3.12 JavaBeans
JavaBeans je standard za kreiranje softverskih komponenti koje imaju svoje
osobine i ponaanje i koje se mogu koristiti u okviru RAD (Rapid Application
Development) alata kao to su Borland JBuilder, Symantec Visual Caf, itd.
Svaka JavaBean komponenta ima svoja svojstva (properties) i reaguje na neke
dogaaje (events). Formalno posmatrano, JavaBean je svaka Java klasa za koju
vai:
1. Ima podrazumevani konstruktor (konstruktor bez parametara koji je
public).
2. Za svako svojstvo koji se zove xxx moraju da postoje public metode
setXxx i getXxx (obratite panju na odnos velikih i malih slova!). Atribut
klase koji bi sadrao vrednost tog svojstva nije obavezan!
3. Za svaki dogaaj predstavljen klasom xxxEvent na koji komponenta
moe da reaguje, moraju da postoje metode addxxxListener(XxxListener) i
removeXxxListener(XxxListener).
63
java.awt.*;
java.awt.event.*;
javax.swing.*;
class UserDefinedBean extends JComponent {
public UserDefinedBean() {
}
public void setText(String s) {
text = s;
}
public String getText() {
return text;
}
public void paint(Graphics g) {
Dimension s = getSize();
g.setColor(Color.black);
g.drawRect(0, 0, s.width - 1, s.height - 1);
g.drawString(text, 2, 12);
}
public Dimension getPreferredSize() {
int width = 70, height = 20;
Graphics g = getGraphics();
FontMetrics fm = null;
if (g != null)
fm = g.getFontMetrics();
if (fm != null) {
width = fm.stringWidth(text);
height = fm.getHeight();
}
return new Dimension(width + 5, height + 2);
}
/** Ovde uvamo property text */
private String text = "text1";
}
b)
a)
65
Poglavlje 4
program B
(147.91.177.196, 7534)
(204.1.177.96, 9000)
66
vor Y (204.1.177.96)
7534
9000
program A
program B
8080
7826
program C
vor Z (147.91.177.195)
program D
9864
Prekid komunikacije treba zavriti propisnim zatvaranjem konekcije. Zatvaranje konekcije se nejee svodi na zatvaranje ulaznog i izlaznog stream-a i
zatvaranje socket-a. Sledi primer:
out.close();
in.close();
sock.close();
68
// komunikacija
out.println(zahtev);
// aljem zahtev
String response = in.readLine(); // itam odgovor
// i tako potreban broj puta...
// prekid veze
in.close();
out.close();
s.close();
69
// itam zahtev
// aljem odgovor
// prekid veze
in.close();
out.close();
s.close();
71
72
Socket sock;
int value;
BufferedReader in;
PrintWriter out;
73
74
Poglavlje 5
75
76
actionListener
chatData
writerThread
outputStream
actionPerfomed()
setMessage()
notify()
writeln()
readerThread
inputStream
textArea
readLine()
append()
77
InputStream
OutputStream
BufferedReader
PrintWriter
ChatClient
ReaderThread
ChatData
WriterThread
ChatClient
JTextField
LoginDlg
JButton
JTextArea
ReaderThread
78
readerThread
inputStream
ClientUtils
activeClient
writerThread
outputStream
readLine()
sendMessageToAll()
setMessage()
notify()
writeln()
Slika 5.8. itaka nit prima poruku i prosleuje je svim pisakim nitima
serverListener
serverSocket
accept()
create()
inputStream
create()
outputStream
create()
activeClient
create()
create()
readerThread
writerThread
Slika 5.9. Uspeno prijavljivanje klijenta i kreiranje posveene itake i pisake niti
ServerListener
PrintWriter
BufferedReader
WriterThread
ActiveClient
ClientUtils
ReaderThread
80
Poglavlje 6
81
82
"vta", "vta");
PREDMETI
NASTAVNIK_ID = NASTAVNIK_ID
PREDMET_ID
NAZIV
PREDAJE
PREDMET_ID
INTEGER
NASTAVNIK_ID
INTEGER
INTEGER
VARCHAR2(150)
PREDMET_ID = PREDMET_ID
= conn.createStatement();
83
pozivom metode next klase ResultSet tekui red e biti prvi red tabele rezultata
(ukoliko tabela sadri bar jedan red). Metoda next vraa boolean vrednost koja
oznaava da li novi tekui red postoji ili ne. Tipino se rezultat upita ita u petlji
kao u sledeoj primeru:
while (rset.next()) {
// ovde itamo red po red rezultata
}
}
catch (Exception ex) {
ex.printStackTrace();
}
84
Vidimo da je u pitanju praktino ista naredba koja se ponavlja vie puta: njena
struktura je ista, a razlikuju se samo podaci koji se u njoj pojavljuju. Slanje
ovakvih naredbi pomou Statement objekta i metode executeUpdate e eljeni
posao obaviti potpuno korektno. Svaki poziv executeUpdate e jednu ovakvu
SQL naredbu slati serveru. Server e, po prijemu naredbe, nju parsirati,
analizirati i formirati nekakav plan njenog izvravanja. Nakon toga e tu
85
86
try {
// uitavanje Oracle drajvera
Class.forName("oracle.jdbc.driver.OracleDriver");
// konekcija
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:@branko.tmd.ns.ac.yu:1526:VTA",
"vta", "vta");
// dodavanje novih nastavnika
PreparedStatement stmt = conn.prepareStatement(
"insert into nastavnici "+
"(nastavnik_id, ime, prezime, zvanje) "+
"values (?, ?, ?, ?)");
stmt.setInt(1, 4);
stmt.setString(2, "Sima");
stmt.setString(3, "Simi");
stmt.setString(4, "docent");
stmt.executeUpdate();
stmt.setInt(1, 5);
stmt.setString(2, "Vasa");
stmt.setString(3, "Vasi");
stmt.setString(4, "docent");
stmt.executeUpdate();
stmt.close();
conn.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
87
procedura
server
SQL naredbe
a)
klijent
procedura
server
b)
Slika 6.2. a) procedura koja operie nad bazom pisana u jeziku klijenta
b) poziv ekvivalentne uskladitene procedure na serveru
89
}
catch (Exception ex) {
ex.printStackTrace();
}
Dakle, na kraju svake transakcije koja se tipino nalazi u try/catch bloku mora se
pozvati commit metoda. Ukoliko se dogodilo neto nepredvieno u toku
izvravanja operacije to je rezultovalo izuzetkom, poziva se metoda rollback u
okviru catch sekcije. Metoda rollback takoe moe da izazove izuzetak, tako da je
i ona morala biti smetena u poseban try/catch blok.
90
U pitanju je poziv statike metode forName klase Class. Instance klase Class
predstavljaju klase i interfejse pokrenute Java aplikacije. Dakle, za svaku klasu i
interfejs koja se koristi u programu postoji po jedna instanca klase Class koja je
opisuje. Klasa Class sadri metode pomou kojih se mogu, u toku izvravanja
programa, dobiti informacije o metodama, atributima i ostalim karakteristikama neke konkretne klase koja se koristi u programu. Statika metoda forName
vraa inicijalizovan objekat klase Class koji odgovara klasi iji je naziv dat
parametrom metode. Inicijalizacija ovakvog Class objekta obuhvata i inicijalizaciju statikih atributa klase koja se uitava u JVM. Inicijalizacija statikih
atributa klase moe se smestiti u poseban static blok u okviru klase koji ne
pripada nijednoj metodi. Sledi primer jedne klase koja sadri takav blok:
class Test {
static int attr;
static {
// ovde se vri inicijalizacija statikih atributa
attr = 0;
}
}
91
92
Poglavlje 7
Klijent
Mrea
Klijent
Server
Klijent
93
94
Web ita
Mrea
Web ita
Web ita
Klijent
aplikacija
Aplikacioni
server
SUBP
95
Ovakav koncept je doveo do podele programskog koda na segmente koji implementiraju tano odreene funkcije sistema. Tako organizovan sistem je
jednostavniji za odravanje, jer je mogue nezavisno razvijati korisniki
interfejs, i logiku aplikacije. Za potrebe fizikog rukovanja podacima najee se
koristi neki od komercijalno dostupnih servera za tu namenu.
Troslojne arhitekture informacionih sistema podrazumevaju oslanjanje na
standarde u odgovarajuom oblastima. Najee su u pitanju sistemi zasnovani
na Internet tehnologijama. Oslanjanje na standarde omoguava integraciju
informacionih sistema heterogenih u pogledu koriene hardverske i softverske
opreme. Na primer, raunarska mrea ovakvog sistema moe biti zasnovana na
TCP/IP familiji protokola. Serveri u mrei mogu biti od razliitih proizvoaa,
sve dok obezbeuju standardne servise predviene protokolom.
Druga vana karakteristika troslojnih sistema je skalabilnost. Pre svega,
poveavanje broja klijenata je jednostavno. Poveavanje propusne moi servera
srednjeg sloja je mogue kroz dodavanje novih serverskih maina. Analogno
tome mogue je poveati i propusnu mo zadnjeg sloja. Slika 7.4 prikazuje
jednu od moguih konfiguracija ovakvog sistema. Ovde je vano primetiti da se
poveanje brzine odziva serverskog sloja moe postii dodavanjem novih
serverskih maina uz korienje postojeih. Na taj nain moe se iskoristiti i
oprema koja ne mora imati vrhunske performanse. Sistem sa vie servera
karakterie i poveana pouzdanost i fleksibilnost. Logika aplikacije se moe
menjati i u toku rada sistema. Pored toga, mogue je efikasno vriti balansiranje
optereenja serverskog podsistema.
Daljim proirivanjem koncepta troslojnih sistema dolazi se do pojma vieslojnih
sistema (multitier architecture), gde se vri dalja podela na komponente u okviru
srednjeg sloja sa ciljem jo veeg poveanja skalabilnosti, odnosno performansi.
Klijent
Klijent
Aplikacioni server
SUBP
Klijent
Aplikacioni server
SUBP
Klijent
Aplikacioni server
Klijent
96
Web browser
App server
Web Server
DBMS
Web browser
Web Server
App server
Web browser
DBMS
Web Server
App server
Web browser
HTML
HTTP
RMI / IIOP
JDBC
RDBMS
RDBMS
97
98
Poglavlje 8
HTTP server
GET /docs.html HTTP/1.0
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Accept-Cookies: yes
Host: branko.tmd.ns.ac.yu
...
Zahtev koji klijent upuuje serveru je tekstualna poruka koja sadri vie redova.
Prvi red poruke je najvaniji: on sadri komandu koju klijent upuuje serveru (u
ovom sluaju to je GET komanda kojom se zahteva odreena datoteka sa Web
servera), putanju datoteke u okviru Web sajta servera (/docs.html) i oznaku
verzije protokola (HTTP/1.0). Naredni redovi u poruci predstavljaju dodatne
informacije koje server moe da iskoristi za svoje potrebe. U ovom primeru,
polje User-Agent predstavlja opis klijentskog softvera (tip i verzija Web itaa i
operativnog sistema), polje Host predstavlja simboliku adresu klijenta, itd.
GET je samo jedna od komandi koje moe da uputi klijent. Ujedno je to i daleko
najee upotrebljavana komanda. U nastavku teksta bie rei i o nekim drugim
komandama.
Zadatak servera je da po prijemu ovakvog zahteva odgovori na njega. U ovom
sluaju odgovor servera treba da sadri traenu datoteku (docs.html), pri emu
je format odgovora takoe definisan HTTP protokolom. Slika 8.2 prikazuje
situaciju kada server alje odgovor klijentu.
99
HTTP klijent
HTTP server
HTTP/1.0 200 OK
Content-Type: text/html
<HTML>
<HEAD>
...
Prvi red odgovora sadri oznaku protokola, trocifreni broj koji predstavlja
status izvrene operacije (u ovom sluaju to je 200), i tekstualni opis tog statusa
(OK). Konstanta 200 oznaava da je zahtev uspeno izvren i da se traena
datoteka nalazi u nastavku poruke. Druge konstante koje se ee sreu su 404
(traena datoteka nije pronaena), 407 (pristup datoteci nije dozvoljen), i 302
(datoteka premetena na drugo mesto). Konstante su definisane HTTP
protokolom.
Sledei red odgovora (Content-Type) je oznaka tipa sadraja koji se vraa. U
pitanju su standardizovane oznake propisane u odgovarajuim RFC dokumentima. Na primer, HTML datoteke imaju oznaku text/html, datoteke sa ASCII
tekstom bez formatiranja imaju oznaku text/plain, GIF slike image/gif, JPEG slike
image/jpeg, itd. Neposredno pre sadraja datoteke koja se alje nalazi se jedan
prazan red koji razdvaja zaglavlje odgovora od samog sadraja datoteke.
Ukupna sekvenca aktivnosti klijenta i servera u HTTP komunikaciji je sledea:
1.
2.
3.
4.
100
HTTP klijent
HTTP server
Sa slike se vidi da, po prijemu zahteva klijenta, server uitava traenu datoteku
iz svog fajl-sistema i alje je nazad klijentu preko mree.
Dinamiki sadraji nisu uskladiteni unapred ve se generiu za svaki zahtev
klijenta posebno. Sekvenca dogaaja kada klijent zatrai neki dinamiki
generisanu datoteku je prikazana na slici 8.4.
1) klijent zahteva fajl
HTTP klijent
HTTP server
U ovom sluaju server nee traiti datoteku u okviru fajl-sistema; na neki nain
server zna da je u pitanju dinamiki generisana datoteka i poziva
odgovarajui potprogram koji e je generisati. Najee nema potrebe ovako
generisanu datoteku uvati na serveru; ona se zato nee uvati u okviru fajlsistema servera.
8.3 Servleti
Servleti su jedna od tehnologija za generisanje dinamikih Web sadraja. Da bi
se servleti mogli koristiti, Web server mora da ima odgovarajuu podrku za
servlete. Pisanje servleta je mogue samo u programskom jeziku Java, tako da je
za njihovo izvravanje potrebna i JVM (koju najee obezbeuje Web server).
Servlet je, zapravo, Java klasa koja nasleuje standardnu klasu HttpServlet.
Klase i interfejsi koji se koriste u pisanju servleta nalaze se u paketima
javax.servlet i javax.servlet.http. Mogli bismo da napiemo klasu koja nasleuje
HttpServlet i ne dodaje ili redefinie nita; takav servlet bio bi funkcionalan ali
ne bi radio nita korisno. Dodavanje funkcionalnosti u servlet postie se
redefinisanjem sledeih metoda (spisak nije potpun, ovde su pobrojane samo
najee koriene metode):
init
destroy
doGet
doPost
101
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<HTML>");
out.println("<HEAD>");
out.println("<TITLE>Hello World Servlet</TITLE>");
out.println("</HEAD>");
out.println("<BODY>Hello World!</BODY>");
out.println("</HTML>");
Poslednji segment URL-a predstavlja naziv servlet klase. Ovo se, kod veine
Web servera, moe konfigurisati na odreeni nain. Upuivanjem Web itaa na
ovakvu adresu dobie se rezultat kao na slici 8.5.
103
import
import
import
import
import
javax.servlet.*;
javax.servlet.http.*;
java.io.*;
java.util.Enumeration;
java.util.Vector;
104
105
Princip rada cookie mehanizma prikazan je na slici 8.6. Prilikom slanja prvog
zahteva server e ustanoviti da mu klijent nije poslao cookie kao jednu stavku u
zaglavlju zahteva. U odgovor na taj zahtev server e dodati cookie. Ukoliko je
Web ita podeen tako da radi sa cookie-ima, on e u svim sledeim zahtevima
koje bude slao tom serveru ukljuiti i cookie, tako da e server moi da prepozna
klijenta koga je ve ranije opsluivao. ta je jedan cookie zapravo? Moemo ga
shvatiti kao string od tipino 20-30 nerazumljivih znakova koji je namenjen za
jednoznano identifikovanje korisnika na serveru.
1) zahtev
HTTP klijent
2) odgovor + cookie
HTTP server
a)
1) zahtev + cookie
HTTP klijent
2) odgovor + cookie
HTTP server
b)
Slika 8.6. a) slanje prvog zahteva i prijem odgovora koji ukljuuje cookie
b) svi sledei zahtevi sadre cookie
106
107
b)
a)
Prva stranica je statika HTML stranica, iji sadraj sledi (elementi forme su
naglaeni, ostatak predstavlja formatiranje):
<HTML>
<HEAD>
<TITLE>Primer sa formom</TITLE>
</HEAD>
<BODY>
<h1>Unesite podatke</h1>
<form action="FormTest">
<table cellspacing=0 cellpadding=3 border=0>
<tr>
<td align=right>Ime:</td>
<td><input type="text" name="ime"></td>
</tr>
<tr>
<td align=right>Prezime:</td>
<td><input type="text" name="prezime"></td>
</tr>
<tr>
<td align=right> </td>
<td><input type="submit" value=" Posalji "></td>
</tr>
</table>
</form>
</body>
</html>
Atribut action taga form koji obuhvata elemente forme (polja za unos i dugme)
navodi stranicu na koju e se prei kada korisnik klikne na dugme Poalji. U
pitanju je naziv servleta koji e prikazati narednu stranicu. Zahtev koga e Web
108
ita poslati u tom trenutku imae ugraene i podatke iz forme. Podaci e biti
navedeni u sledeem obliku:
ime=Branko&prezime=Milosavljevic
Druga mogunost je da se parametri poalju kao deo zahteva koji sadri POST
komandu:
POST /kurs/FormTest HTTP/1.0
...
ime=Branko&prezime=Milosavljevic
a)
b)
Slika 8.8. a) stranica dobijena GET zahtevom
b) stranica dobijena POST zahtevom
109
Podaci uneti u formi su vidljivi u adresi stranice kada se koristi GET zahtev, to
moe biti neprikladno u sluaju da su podaci poverljivi. Iz tog razloga se
najee i koristi POST metod za prenos parametara.
Mesto gde se navodi metod prenosa parametara je polazna HTML stranica. Tag
form ima atribut method za tu namenu. Atribut moe imati vrednosti get i post, a
ako se izostavi podrazumeva se vrednost get. Sledi primer:
<form action="FormTest" method="post">
javax.servlet.*;
javax.servlet.http.*;
java.sql.*;
java.util.*;
java.io.*;
111
HttpServletResponse res)
throws ServletException, IOException {
try {
// postavljanje upita
String query = "SELECT ime, prezime FROM nastavnici";
Statement stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery(query);
Vector imena = new Vector();
Vector prezimena = new Vector();
while (rset.next()) {
imena.addElement(rset.getString(1));
prezimena.addElement(rset.getString(2));
}
rset.close();
stmt.close();
// generisanje HTML stranice
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><body>");
out.println("<h3>Nastavnici u bazi:</h3>");
out.println("<pre>");
for (int i = 0; i < imena.size(); i++)
out.println(
(String)(imena.elementAt(i)) + " " +
(String)(prezimena.elementAt(i)));
out.println("</pre></body></html>");
out.flush();
} catch (Exception ex) {
// generisanje HTML stranice sa opisom greke
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><body><pre>");
out.println("Dogodila se greska:<br> " +
ex.toString() + "</pre></body></html>");
out.flush();
}
}
Ovaj servlet ima redefinisanu metodu init, u okviru koje se uitava JDBC
drajver i otvara konekcija sa bazom podataka. Ova konekcija je atribut klase,
tako da je dostupna iz svih metoda, ukljuujui i doGet. U okviru metode destroy
konekcija se zatvara. Ovakav koncept znai da e jedan servlet koristiti tano
jednu konekciju za sve zahteve. To moe biti reenje koje je zgodno naroito
kada se softver za upravljanje bazom podataka licencira po aktivnoj konekciji
u ovom sluaju neogranien broj korisnika Web sajta sa ovim servletom imao bi
112
pristup bazi podataka preko samo jedne konekcije. Meutim, treba imati u vidu
da su mogue situacije kada se ista konekcija koristi za operacije nad bazom
podataka iz vie programskih niti. Neki JDBC drajveri, kao to je Oracle, ne
doputaju izvravanje DML operacija iz vie niti istovremeno preko iste
konekcije, tako da ovo reenje ima smisla samo kada se postavljaju upiti (oni se
mogu paralelno izvravati iz vie niti).
Alternativa ovom reenju je da se prilikom svake obrade GET zahteva otvara
nova konekcija, koja se odmah zatim i zatvara. Ova varijanta je izuzetno
neefikasna, jer je otvaranje konekcije sa bazom podataka dugotrajna operacija.
Jo jedno reenje ovog problema bie prikazano u okviru poglavlja 11.
113
Poglavlje 9
114
Izraz new java.util.Date() predstavlja kreiranje novog objekta klase Date. Takav
objekat se inicijalizuje na tekui datum i vreme u trenutku kreiranja. Kako je u
pitanju izraz koji nije tipa String, prilikom ugradnje ove vrednosti u stranicu
bie pozvana metoda toString klase Date koja e obezbediti odgovarajuu string
reprezentaciju ovog objekta. Konaan rezultat je stranica koja e prilikom
svakog pristupa prikazati datum i vreme kada je pristup izvren.
9.2.2 Skriptleti
JSP skriptleti su elementi koji se pojavljuju izmeu znakova <% i %>. Njihov
sadraj je proizvoljan segment Java programskog koda. Ovako ugraen
programski kod direktno se ugrauje u kod generisanog servleta na odgovarajue mesto! Sledi primer jedne JSP stranice:
<html>
...
<% if (Math.random() < 0.5) { %>
Dobar dan!
<% } else { %>
115
Dobro vee!
<% } %>
...
</html>
Moe se rei da se svaki red JSP stranice koji sadri samo klasine HTML
oznake konvertuje u jedan out.println(...) iskaz u servletu. JSP skriptleti se
ugrauju u servlet na odgovarajuem mestu direktnim kopiranjem datog
teksta.
Jedna od posledica ovakvog tretiranja skriptleta je i da opseg vaenja promenljivih definisanih u skriptletima moe da se protee kroz vie skriptleta. Sledi
primer koji ilustruje ovu osobinu:
<table border=1>
<%
String names[] = { "Bata", "Pera", "Mika", "Laza", "Sima" };
for (int i = 0; i < names.length; i++) {
%>
<tr>
<td><%= i %></td>
<td><%= names[i] %></td>
</tr>
<% } %>
</table>
Gornji primer proizvodi HTML tabelu koja ima onoliko redova koliko je
elemenata niza names. Promenljiva names i broja petlje i dostupni su i u
narednim skriptletima. Generisani HTML kod koga e primiti Web ita
izgledao bi ovako:
<table border=1>
<tr>
<td>0</td>
<td>Bata</td>
</tr>
<tr>
<td>1</td>
116
<td>Pera</td>
</tr>
<tr>
<td>2</td>
<td>Mika</td>
</tr>
<tr>
<td>3</td>
<td>Laza</td>
</tr>
<tr>
<td>4</td>
<td>Sima</td>
</tr>
</table>
9.2.3 Deklaracije
JSP deklaracije su elementi koji se pojavljuju izmeu znakova <%! i %>.
Deklaracije sadre tekst koji e u rezultujui servlet biti smeten na mestu
atributa ili metode servlet klase. Na primer, deklaracija
<%! int hitCount = 0; %>
Ovakav atribut servlet klase je dostupan u viestrukim pozivima doGet ili doPost
metode servleta. Primer u odeljku 8.6 ilustrovao je upotrebu ovakvog atributa.
Ovde navodimo primer koji slian efekat postie u JSP okruenju.
<%! int hitCount = 0; %>
<html>
<body>
<h3>Primer JSP deklaracije</h3>
Ovoj stranici je ukupno pristupano
<%= ++hitCount %> puta.
</body>
</html>
Ovaj primer ne koristi sinhronizovan pristup atributu hitCount zbog jednostavnosti. Metoda inc primera iz odeljka 8.6 se takoe moe dodati kao JSP
deklaracija. Poloaj JSP deklaracije u okviru JSP stranice nije bitan.
9.2.4 Direktive
JSP direktive su elementi smeteni izmeu znakova <%@ i %>. Namenjene su
za obavljanje nekih specifinih zadataka u okviru JSP stranice. Ovde emo
navesti neke primere.
<%@ page import="java.util.*" %>
117
118
Forma sadri dva polja iji su nazivi, recimo, username i password. Sadraj ovih
polja bie prenet kroz parametre novoj stranici nakon klika na dugme Poalji. U
okviru te nove stranice moemo smestiti ove podatke u sledeu JavaBean
komponentu:
public class User {
public void setUsername(String x) {
username = x;
}
public void setPassword(String x) {
password = x;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
private String username;
private String password;
}
Evo kako bi mogla da izgleda JSP stranica na koju se prelazi klikom na dugme
Poalji:
<jsp:useBean id="user" class="somepackage.User"/>
<jsp:setProperty name="user" property="username" param="username"/>
<jsp:setProperty name="user" property="password" param="password"/>
<html>
...
</html>
119
<% } %>
...
</html>
Primer pretpostavlja da klasa User ima definisanu metodu login koja vraa
logiku vrednost. Metoda login, na primer, moe da proveri u bazi podataka da
li korisnik sa datim imenom i lozinkom postoji. Na osnovu takve provere u
stranici moe da se ispie odgovarajui tekst.
Vrednost atributa
Opis
application
session
request
page
svaka stranica ima svoju instancu, bez obzira na korisnike koji joj pristupaju
Tabela 9.2. Mogue vrednosti atributa scope taga jsp:useBean.
120
121
122
Poglavlje 10
klijent
3) rezultat
123
klijent
serverski
objekat
klijent aplikacija
kontejner
10.2 RMI
interfejs
server
objekat
klijent
objekat
RMI
interfejs
client
stub
server
skeleton
RMI
objekat
JVM 2
JVM 1
Klijent objekat pristupa serverskom preko RMI interfejsa. Sa strane klijenta, taj
interfejs implementira stub klasa. Osnovni zadatak te klase je da pozive metoda
serverskog objekta koje upuuje klijentski objekat prosledi preko mree. Na
serverskoj strani skeleton klasa ima ekvivalentnu funkciju: da pozive pristigle
preko mree pretvori u pozive stvarnog serverskog objekta. Stub i skeleton klase
se generiu posebnim alatom iz JDK paketa, rmic kompajlerom.
10.2.1 Faze u pisanju RMI programa
Pisanje RMI programa se moe podeliti u nekoliko faza:
1. pisanje RMI interfejsa
2. pisanje RMI serverskog objekta
3. pisanje RMI klijenta
124
(.java)
Implementiranje RMI
interfejsa - definisanje RMI
klase
(.java)
Implementiranje klijentske
klase
javac
(.class)
(.java)
javac
rmic
(.class)
klijent
(.class)
(.class)
RMI interfejs
(.class)
client stub
(.class)
(.class)
server skeleton
(.class)
RMI objekat
(.class)
125
objekat
serijalizacija
byte
stream
byte
stream
deserijalizacija
JVM 1
kopija
objekta
JVM 2
126
java.rmi.*;
java.rmi.registry.*;
java.rmi.server.*;
java.net.*;
127
128
4. startuje se server:
java Server
5. startuje se klijent:
java Client
10.3 CORBA
10.3.1 Osnovne odrednice
Common Object Request Broker Architecture (CORBA) predstavlja arhitekturu i
tehnologiju distribuiranih objekata koja je zamiljena tako da bude nezavisna
od korienog programskog jezika (mogua je komunikacija izmeu objekata
pisanih u razliitim jezicima). Pored toga, CORBA je nezavisna i od konkretnog
proizvoaa softvera: ona zapravo predstavlja standard OMG (Object Management Group) grupe. Proizvoai softvera mogu da nude svoje implementacije
zajednikog standarda. Ujedno je i najsloenija tehnologija koja se bavi
distribuiranim objektima, jer podrava najiri spektar zahteva u okviru ovog
domena.
Neto manja dobra osobina CORBA je to je u pitanju standard, koji se sporo
usvaja. Zbog sloenosti i velikog broja mogunosti relativno je komplikovana za
uenje. U ranijim verzijama CORBA standarda mogua je bila i nekompatibilnost izmeu proizvoda razliitih proizvoaa.
Polazna ideja je identina kao i kod RMI tehnologije: klijentski objekat poziva
metode serverskog objekta na isti nain kao da je u pitanju lokalni objekat. (U
CORBA terminologiji serverski objekat se naziva servant).
Klijent i servant meusobno ne komuniciraju direktno, ve preko posrednika
nazvanih ORB (Object Request Broker). ORB je softverski modul koji je namenjen
za mrenu komunikaciju. ORB-ovi izmeu sebe komuniciraju po IIOP (Internet
129
ORB
servant
IIOP
ORB
10.3.2 IDL
Kako CORBA omoguava korienje objekata pisanih u razliitim programskim
jezicima, mora postojati univerzalan nain za specificiranje interfejsa serverskih
objekata kojima klijenti mogu da pristupe. Za tu namenu CORBA definie svoj
Interface Definition Language (IDL) jezik.
Na osnovu specifikacije interfejsa u IDL jeziku odgovarajuim alatom se
generie implementacija servanta u konkretnom programskom jeziku. IDL
mapiranja su definisana za mnoge jezike (Java, C++, Smalltalk, COBOL, itd) i
predstavljaju deo OMG standarda.
Mapiranje IDL-a na programski jezik Java je relativno jednostavno: koncept IDL
modula se mapira na Java paket; IDL interfejs se mapira na Java interfejs; slino
vai i za metode i atribute. Sledi primer jednog IDL interfejsa i odgovarajueg
Java interfejsa:
module counter {
interface Count {
attribute long sum;
long increment();
};
};
package counter;
interface Count {
int sum ();
void sum (int newSum);
int increment ();
}
Generisanje Java interfejsa na osnovu IDL interfejsa obavlja idlj, alat iz JDK
paketa.
Rezultat rada idlj alata nije samo Java interfejs, nego i stub i skeleton klase. Ove
klase koriste ORB za komunikaciju sa objektom sa druge strane. Slika 10.8
prikazuje meusobni odnos klijent, servant, stub, skeleton i ORB modula.
klijent
servant
client stub
server
skeleton
ORB
IIOP
ORB
130
Interfejs sadri jedan atribut, sum, i jednu metodu, increment. Sada treba
generisati odgovarajui Java kod za dati IDL interfejs. Komanda kojom se to
postie glasi:
idlj fall counter.idl
_CountImplBase.java: Klasa koju treba da nasledi servant klasa. Implementira CORBA funkcionalnost koju autor servanta ne mora da poznaje.
idlj
_Count
_stub.java
Count
Helper.java
_Count
ImplBase
.java
Count
Holder.java
Count.java
klijent
Count
Operations.
java
server
132
CounterServer.java
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import counter.*;
public class CounterServer {
public static void main(String[] args) {
try {
// inicijalizuj ORB, prosledi mu command-line parametre
ORB orb = ORB.init(args, null);
// Kreiraj servanta i registruj ga kod ORB-a
CounterServant countRef = new CounterServant();
orb.connect(countRef);
// pokupi osnovni Naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// umesto klasinog kastovanja, ovde se poziva narrow
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// Registruj servant objekat kod naming servisa
NameComponent nc = new NameComponent("Counter", "");
NameComponent path[] = {nc};
ncRef.rebind(path, countRef);
// ekaj na pozive klijenata; program je ovde
// blokiran beskonano dugo
System.out.println("Waiting for clients");
java.lang.Object sync = new java.lang.Object();
synchronized(sync){
sync.wait();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
CounterClient.java
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import counter.*;
public class CounterClient {
public static void main(String[] args) {
try {
// inicijalizuj ORB, prosledi mu command-line parametre
ORB orb = ORB.init(args, null);
// pokupi osnovni Naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// umesto klasinog kastovanja, ovde se poziva narrow
NamingContext ncRef = NamingContextHelper.narrow(objRef);
133
Interfejs Reader definie novi tip izuzetka badRead. Metoda read ovog interfejsa
moe da izazove taj izuzetak. IDL izuzetak badRead je predstavljen klasom
reader.ReaderPackage.badRead. Klase iz ovog paketa su takoe dobijene upotrebom idlj alata. Slika 10.10 prikazuje lokaciju dodatnik klasa vezanih za
izuzetak badRead.
134
135
136
client
servant
server
servant
client app
server app
4)
2)
naming
service
1)
137
logiju moe instalirati u razliita EJB okruenja bez modifikacije. EJB specifikacija je prola kroz nekoliko verzija (1.0, 1.1, 2.0), gde je svaka od verzija
dodavala nove mogunosti. U pitanju je tehnologija koja se implementira
iskljuivo u jeziku Java.
Sama EJB specifikacija definie model serverskih komponenti za razvoj vieslojnih arhitektura sa distribuiranim objektima. Vano je primetiti da se EJB ne
bavi klijentskom stranom. EJB definie okruenje u kome ive EJB komponente. Okruenje ine EJB server i EJB kontejner. U tekuoj verziji specifikacije
funkcije EJB servera i EJB kontejnera nisu strogo razdvojene. U principu je
zamiljeno da EJB server i kontejner budu nezavisni jedan od drugog to bi
omoguilo izgradnju EJB okruenja kombinovanjem komponenti razliitih
proizvoaa. Za sada to u praksi nije mogue, tako da svaki proizvoa nudi
svoj par server/kontejner koji se ne moe razdvojiti. Slika 10.12 prikazuje
meusobni odnos EJB servera, kontejnera i komponenti.
EJB komponenta
EJB komponenta
EJB kontejner
EJB server
Session beans
o stateless
o stateful
Entity beans
o bean-managed persistence
o container-managed persistence
S obzirom na ove osobine session i entity beans komponenti, generalna preporuka je da klijenti direktno komuniciraju samo sa session beans komponentama,
koje implementiraju potrebne operacije. Podaci kojima te operacije rukuju
predstavljeni su odgovarajuim entity beans komponentama, koje za potrebe
trajnog uvanja svog stanja koriste bazu podataka. Slika 10.12 prikazuje ovaj
meusobni odnos klijenata, session i entity bean komponenti i baze podataka u
okviru EJB okruenja prema njihovoj zamiljenoj nameni.
139
klijent
session
bean
entity
bean
session
bean
entity
bean
DB
klijent
EJB kontejner
EJB server
Slika 10.12. Meusobni odnos klijenata, session i entity bean komponenti i baze podataka
RMI klijent
EJB komponenta
CORBA klijent
EJB komponenta
COM+ klijent
EJB kontejner
EJB server
Klijent komunicira sa komponentom iskljuivo preko njenih home i remote interfejsa. Meusobni odnos ovih delova EJB komponente prikazuje slika 10.14.
140
home
interfejs
bean
klasa
remote
interfejs
klijent
EJB kontejner
EJB server
141
Klijent koji koristi ovakvu EJB komponentu putem RMI tehnologije se,
praktino ne razlikuje od klasinog RMI klijenta. Jedino ostaje izbor naina
dobijanja reference na komponentu: putem RMI klase java.rmi.Naming ili putem
JNDI biblioteke. Ovde navodimo primere za obe varijante.
import javax.ejb.*;
import java.rmi.*;
import demo.*;
public class DemoClient1 {
public static void main(String[] args) {
try {
System.setSecurityManager(new RMISecurityManager());
DemoHome demoHome = (DemoHome)Naming.lookup(
"//branko.tmd.ns.ac.yu:1099/demo");
142
143
Poglavlje 11
Front office: sajt koji je dostupan svim korisnicima; omoguava pregledanje kataloga proizvoda i njihovo naruivanje.
Back office: sajt koji je dostupan samo administratorima sistema; omoguava izvravanje administrativnih funkcija.
Servlets
JSP
144
odgovara Oracle SUBP je dat na slici 11.3. Ovaj model podataka dele front office i
back office aplikacije.
Admin
Supplier
Admin ID
Username
Pasword
Supplier ID
Supplier Name
Supplier Address
Supplies
User
User ID
Username
Pasword
First Name
Last Name
User Address
Email
Receive news
Order
Order ID
Order Date
Places
Product
Ordered Item
Quantity
Contains Items
Is Parent To
Is Ordered
Product ID
Product Name
Vendor
Description
Price
Contains Products
Category
Category ID
Category Name
Category Desc
Has Images
Product Image
Image ID
Image Title
Image Height
Image Width
Content Type
Image Data
SUPPLIERS
SUPPLIER_ID
SUPPLIER_NAME
SUPPLIER_ADDRESS
INTEGER
VARCHAR2(100)
VARCHAR2(100)
not null
not null
not null
CATEGORY_ID = PARENT_CATEGORY_ID
SUPPLIER_ID = SUPPLIER_ID
ORDERS
ORDER_ID
USER_ID
ORDER_DATE
PRODUCTS
ORDERED_ITEMS
ORDER_ID = ORDER_ID
ORDER_ID
PRODUCT_ID
QUANTITY
PRODUCT_ID = PRODUCT_ID
PRODUCT_ID
CATEGORY_ID
SUPPLIER_ID
PRODUCT_NAME
VENDOR
DESCRIPTION
PRICE
INTEGER
INTEGER
INTEGER
VARCHAR2(100)
VARCHAR2(100)
VARCHAR2(1000)
DECIMAL(9,2)
not null
not null
not null
not null
not null
not null
not null
CATEGORIES
CATEGORY_ID = CATEGORY_ID
CATEGORY_ID
PARENT_CATEGORY_ID
CATEGORY_NAME
CATEGORY_DESC
INTEGER
INTEGER
VARCHAR2(50)
VARCHAR2(500)
not null
null
not null
null
PRODUCT_ID = PRODUCT_ID
USER_ID = USER_ID
PRODUCT_IMAGES
USERS
USER_ID
USERNAME
PASWORD
FIRST_NAME
LAST_NAME
USER_ADDRESS
EMAIL
RECEIVE_NEWS
INTEGER
VARCHAR2(20)
VARCHAR2(20)
VARCHAR2(25)
VARCHAR2(35)
VARCHAR2(100)
VARCHAR2(50)
NUMBER(1)
PRODUCT_ID
IMAGE_ID
IMAGE_TITLE
IMAGE_HEIGHT
IMAGE_WIDTH
CONTENT_TYPE
IMAGE_DATA
ADMINS
not null
not null
not null
not null
not null
not null
not null
not null
ADMIN_ID
USERNAME
PASWORD
INTEGER
VARCHAR2(20)
VARCHAR2(20)
not null
not null
not null
INTEGER
INTEGER
VARCHAR2(50)
INTEGER
INTEGER
VARCHAR2(30)
LONG RAW
not null
not null
null
not null
not null
not null
null
Physical Data Model
Project : Web Shop
Model : Web Shop
Author : Branko Milosavljevic
145
predstavljaju strukturu front office i back office sajta. (Pod strukturom sajta
podrazumevamo graf koji opisuje mogue kretanje izmeu stranica sajta).
login successful
login
index
submit
submit
register
category
product
submit
fromPurchase
purchase
oldPurchase
redirect
login
submit
addPicture
register
savePicture
index
products
submit
suppliers
categories
submit
submit
Tabela 11.1 sadri listu stranica front office sajta sa njihovim parametrima.
Stranica
index.jsp
category.jsp
product.jsp
purchase.jsp
oldPurchase.jsp
login.jsp
register.jsp
Parametar
Znaenje
catID
catID
prodID
quantity
suppliers.jsp
addPicture.jsp
savePicture.jsp
Parametar
Znaenje
name
desc
parent
name
desc
vendor
category
supplier
price
name
address
productID
147
height
width
title
uploaded
username
password
username
password
login.jsp
register.jsp
1
Order
1
-orderID : int = 0
-orderDate : Date
+add() : void
Category
-categoryID : int = 0
-name : String = ""
-desc : String = ""
+add() : void
*
*
Supplier
Item
AdminUser
-adminID : int = 0
-username : String = ""
-password : String = ""
+register() : void
+login() : void
Product
-quantity : int = 0
+add() : void
*
1
-productID : int = 0
-name : String = ""
-vendor : String = ""
-desc : String = ""
+add() : void
-supplierID : int = 0
-name : String = ""
-address : String = ""
+add() : void
*
ProdImage
-imageID : int = 0
-title : String = ""
-height : int = 0
-width : int = 0
-contentType : String = "image/gif"
-data : Byte
+add() : void
Pored toga potrebno je obezbediti da svi korisnici pristupaju istoj instanci poola. To se moe obezbediti na taj nain to e klasa ConnectionPool koja predstavlja
pool sadrati statiki atribut klase ConnectionPool. Taj atribut je, zapravo, jedina
instanca klase koja e biti kreirana. Ova instanca se inicijalizuje u okviru
odgovarajueg static bloka. Klasa ne sadri nijedan javni konstruktor, kako bi se
onemoguilo kreiranje objekata ove klase. Posebna metoda getConnectionPool
vraa statiki atribut klase. Jedini nain da korisnici dobiju objekat ove klase je
pomou poziva ove metode. U nastavku je dat programski kod klase
ConnectionPool.
import java.sql.*;
import java.util.*;
/**
* Handles a pool of JDBC connections. Use
* <code>checkOut</code> to get a connection from
* the pool, and <code>checkIn</code> to put it
* back in the pool when finished.<p>
*
* The class is implemented as a singleton, so
* one must use <code>getConnectionPool()</code>
* to obtain a connection pool reference.
*
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
*
* @version 1.1
*/
public class ConnectionPool {
149
150
return conn;
152
Literatura
Koriena literatura u okviru ovog praktikuma nije posebno referencirana. U
ovom odeljku je dat spisak koriene literature grupisan po tematskim
celinama. Pored toga, Web sajt koji prati kurs sadri vei deo ovih materijala.
Arnold K., Gosling J., Holmes D., Programski jezik Java, tree izdanje, CET,
Beograd 2001.
Eckel B., Thinking in Java, 2nd edition, Prentice-Hall, New Jersey 2000.
Orfali R., Harkey D., Client/Server Programming with Java and CORBA, 2nd
edition, Wiley, New York 1998.
Roman E., Mastering Enterprise JavaBeans and the Java 2 Enterprise Edition,
Wiley, New York 2000.
Hall M., Core Servlets and Java Server Pages, Prentice-Hall, New Jersey
2000.
153
WWW
Brown, M., Special Edition Using HTML, 2nd Edition, QUE Publishing,
Indianapolis 1999.
SQL
Stephens R. K., Plew R. R., Morgan B., Perkins J., Teach Yourself SQL in 21
Days, 2nd Edition, SAMS Publishing, Indianapolis 1999.
154
Prilozi
Veba: chat aplikacija
Reenje je detaljno opisano u poglavlju 5. Ovde se daje samo programski kod
klijentskog i serverskog programa. Sledi programski kod klijenta:
chat\client\ChatClient.java
package chat.client;
import
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.io.*;
java.net.*;
155
}
});
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((d.width - getSize().width) / 2,
(d.height - getSize().height) / 2);
}
/** alje poruku serveru. */
public void sendMessage() {
String message = tfEntryLine.getText().trim();
taMessages.append(">> " + message + "\n");
tfEntryLine.setText("");
cd.setMessage(message);
}
/** Vri prijavljivanje korisnika. */
public boolean login() {
try {
LoginDlg loginDlg = new LoginDlg();
loginDlg.setVisible(true);
InetAddress addr = InetAddress.getByName(loginDlg.getServer());
Socket sock = new Socket(addr, TCP_PORT);
BufferedReader in = new BufferedReader(
new InputStreamReader(
sock.getInputStream()));
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
sock.getOutputStream())), true);
out.println(loginDlg.getUsername());
String response = in.readLine();
if (!response.equals("OK"))
throw new Exception("Invalid user");
cd = new ChatData();
ReaderThread rt = new ReaderThread(sock, in, cd, taMessages);
WriterThread wt = new WriterThread(out, cd);
setTitle("Chat Client [" + loginDlg.getUsername() + "]");
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
return true;
}
/** Prikazuje login dijalog i, ako je prijavljivanje uspeno,
* otvara osnovni prozor aplikacije.
*/
public static void main(String[] args) {
ChatClient cc = new ChatClient();
if (cc.login())
cc.setVisible(true);
else
System.exit(0);
}
JPanel pMessages = new JPanel();
JPanel pEntryLine = new JPanel();
JButton bSend = new JButton("Send");
JButton bClose = new JButton("Close");
JTextField tfEntryLine = new JTextField(25);
JScrollPane spMessages = new JScrollPane();
JTextArea taMessages = new JTextArea();
ChatData cd;
}
chat\client\ChatData.java
package chat.client;
/** Predstavlja bafer za poruke koje se alju serveru. */
public class ChatData {
public synchronized void setMessage(String message) {
this.message = message;
notify();
156
}
public synchronized String getMessage() {
try { wait(); } catch (Exception ex) { }
return message;
}
private String message;
}
chat\client\LoginDlg.java
package chat.client;
import
import
import
import
java.awt.*;
java.awt.event.*;
javax.swing.*;
com.borland.jbcl.layout.*;
157
import java.io.*;
import java.net.*;
import javax.swing.*;
/** Nit za itanje poruka sa servera. */
public class ReaderThread extends Thread {
public ReaderThread(Socket sock, BufferedReader in, ChatData chatData, JTextArea ta) {
this.sock = sock;
this.in = in;
this.chatData = chatData;
this.ta = ta;
start();
}
public void run() {
try {
String msg;
while (true) {
msg = in.readLine();
if (msg != null)
ta.append(msg + "\n");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private
private
private
private
Socket sock;
BufferedReader in;
ChatData chatData;
JTextArea ta;
}
chat\client\WriterThread.java
package chat.client;
import java.io.*;
import java.net.*;
/** Nit za slanje poruka serveru. */
public class WriterThread extends Thread {
public WriterThread(PrintWriter out, ChatData chatData) {
this.out = out;
this.chatData = chatData;
start();
}
public void run() {
try {
String msg;
while (true) {
msg = chatData.getMessage();
out.println(msg);
if (msg.equals("QUIT!"))
System.exit(0);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private Socket sock;
private PrintWriter out;
private ChatData chatData;
}
158
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** Osnovna klasa servera: pokree listener i prikazuje prozor. */
public class ChatServer extends JFrame {
public ChatServer() {
setSize(400, 300);
setTitle("Chat Server");
// the buttons panel
pButtons.setLayout(new FlowLayout());
pButtons.add(bClose);
pButtons.add(bAbout);
// text area for the list of connected clients
spClients.setPreferredSize(new Dimension(390, 200));
spClients.getViewport().setView(taClients);
pClients.add(spClients, BorderLayout.CENTER);
// tabbed pane
tpTabs.add(pClients, "Clients");
tpTabs.add(pStats, "Statistics");
getContentPane().add(tpTabs, BorderLayout.CENTER);
getContentPane().add(pButtons, BorderLayout.SOUTH);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) {
System.exit(0);
}
});
bClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.exit(0);
}
});
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((d.width - getSize().width) / 2,
(d.height - getSize().height) / 2);
}
public static void main(String[] args) {
ChatServer cs = new ChatServer();
ServerListener sl = new ServerListener(cs.taClients);
cs.setVisible(true);
}
JButton bClose = new JButton(" Close ");
JButton bAbout = new JButton(" About... ");
JPanel pButtons = new JPanel();
JPanel pClients = new JPanel();
JPanel pStats = new JPanel();
JTabbedPane tpTabs = new JTabbedPane();
JScrollPane spClients = new JScrollPane();
JTextArea taClients = new JTextArea();
}
chat\server\ServerListener.java
package chat.server;
import java.io.*;
import java.net.*;
import javax.swing.*;
/** Osnovna serverska nit koja eka klijente. */
public class ServerListener extends Thread {
public static final int TCP_PORT = 9000;
public ServerListener(JTextArea ta) {
this.ta = ta;
start();
159
}
public void run() {
try {
ServerSocket ss = new ServerSocket(TCP_PORT);
while (true) {
Socket sock = ss.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(
sock.getInputStream()));
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
sock.getOutputStream())), true);
String username = in.readLine();
String address = sock.getInetAddress().getHostAddress();
ActiveClient client = ClientUtils.addClient(username, address);
if (client == null) {
out.println("Bad user");
in.close();
out.close();
sock.close();
continue;
}
out.println("OK");
ReaderThread rt = new ReaderThread(sock, in, client, ta);
WriterThread wt = new WriterThread(out, client);
ta.setText(ClientUtils.getClientList());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private JTextArea ta;
}
chat\server\ActiveClient.java
package chat.server;
/** Predstavlja jednog aktivnog klijenta. */
public class ActiveClient {
public ActiveClient(String username, String address) {
this.username = username;
this.address = address;
this.message = "";
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setAddress(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
public synchronized void setMessage(String message) {
this.message = message;
notify();
}
public synchronized String getMessage() {
try { wait(); } catch (Exception ex) { }
return message;
}
160
161
Socket sock;
BufferedReader in;
ActiveClient client;
JTextArea ta;
java.io.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.jsp.tagext.*;
162
java.io.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.jsp.tagext.*;
java.io.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.jsp.tagext.*;
/** Tag koji slui kao then deo if naredbe (ako je uslov zadovoljen) */
public class ThenTag extends BodyTagSupport {
public int doStartTag() throws JspTagException {
IfTag parent = (IfTag)findAncestorWithClass(this, IfTag.class);
if (parent == null)
throw new JspTagException("then tag must be inside if tag!");
else if (!parent.hasCondition())
throw new JspTagException("condition tag must be defined before then tag!");
return EVAL_BODY_TAG;
}
163
java.io.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.jsp.tagext.*;
/** Tag koji slui kao else deo if naredbe (ako uslov nije zadovoljen) */
public class ElseTag extends BodyTagSupport {
public int doStartTag() throws JspTagException {
IfTag parent = (IfTag)findAncestorWithClass(this, IfTag.class);
if (parent == null)
throw new JspTagException("else tag must be inside if tag!");
else if (!parent.hasCondition())
throw new JspTagException("condition tag must be defined before else tag!");
return EVAL_BODY_TAG;
}
public int doAfterBody() {
IfTag parent = (IfTag)findAncestorWithClass(this, IfTag.class);
if (!parent.getCondition()) {
try {
BodyContent body = getBodyContent();
JspWriter out = body.getEnclosingWriter();
out.print(body.getString());
} catch (IOException ex) {
System.out.println("Error in ElseTag: " + ex.toString());
}
}
return SKIP_BODY;
}
}
Slede klase koje implementiraju SQLQuery tag i njegove podtagove, ije korienje je prikazano u poglavlju 9. Ovi tagovi se koriste i okviru primera Web
aplikacije iz poglavlja 11.
webshop\sqltags\SQLQueryTag.java
package webshop.sqltags;
import java.io.*;
import java.sql.*;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import webshop.*;
/**
* Main JSP tag for displaying SQL query results. Used with
* <code>OutputStartTag</code>, <code>OutputEndTag</code>,
* <code>OutputTag</code>, and <code>EmptyTag</code> subtags.<p>
* The sequence of calls is as follows:
164
* <ul>
* <li><code>OutputStartTag</code> is rendered once for the first row of
* the result set, if row set is not empty.
* <li><code>OutputEndTag</code> is rendered once for the last row of
* the result set, if row set is not empty.
* <li><code>OutputTag</code> is rendered once for each row of
* the result set, if row set is not empty.
* <li><code>EmptyTag</code> is rendered once if row set is empty.
* </ul>
* This tag defines an attribute called <code>resultRow</code>, an
* instance of <code>java.util.Hashtable</code> for storing a single
* row of the row set. The columns in a row are accessed by
* <code>Hashtable.get()</code> method, with the parameter being the
* name of the column (or alias if defined) in lowercase.
* <p>
* The body of the tag is rendered once for each row in the result set.
*
* @see OutputStartTag
* @see OutputEndTag
* @see OutputTag
* @see EmptyTag
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
* @version 1.0
*/
public class SQLQueryTag extends BodyTagSupport {
/** called at the beginning of the tag */
public int doStartTag() throws JspException {
if (connectionPool == null || sqlQuery == null)
return SKIP_BODY;
int retVal = EVAL_BODY_TAG;
Connection conn = null;
try {
conn = connectionPool.checkOut();
Statement stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery(sqlQuery);
ResultSetMetaData meta = rset.getMetaData();
int columnCount = meta.getColumnCount();
String[] columnNames = new String[columnCount];
for (int i = 0; i < columnCount; i++)
columnNames[i] = meta.getColumnLabel(i+1).toLowerCase();
rows = new Vector();
while (rset.next()) {
Hashtable table = new Hashtable();
for (int i = 0; i < columnCount; i++) {
String colValue = rset.getString(i+1);
colValue = (colValue == null)?"":colValue;
table.put(columnNames[i], colValue);
}
rows.addElement(table);
}
rset.close();
stmt.close();
} catch (SQLException ex) {
ex.printStackTrace();
retVal = SKIP_BODY;
} finally {
connectionPool.checkIn(conn);
}
rowCounter = 0;
if (rows.size() > 0)
pageContext.setAttribute("resultRow",
rows.elementAt(0), PageContext.PAGE_SCOPE);
return retVal;
}
/** called at the end of the tag */
public int doEndTag() throws JspException {
try {
if (bodyContent != null)
bodyContent.writeOut(bodyContent.getEnclosingWriter());
} catch(IOException ex) {
throw new JspException("IO Error: " + ex.getMessage());
}
return EVAL_PAGE;
}
165
166
*
* @see SQLQueryTag
* @see OutputEndTag
* @see OutputTag
* @see EmptyTag
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
* @version 1.0
*/
public class OutputStartTag extends BodyTagSupport {
/** called at the beginning of the tag */
public int doStartTag() throws JspException {
if (parent == null)
return SKIP_BODY;
if (!(parent instanceof SQLQueryTag))
return SKIP_BODY;
SQLQueryTag myParent = (SQLQueryTag)parent;
if (myParent.getRowCounter() == 0 && myParent.getMaxRows() > 0)
return EVAL_BODY_TAG;
else
return SKIP_BODY;
}
/** called at the end of the tag */
public int doEndTag() throws JspException {
try {
if (bodyContent != null)
bodyContent.writeOut(bodyContent.getEnclosingWriter());
} catch(IOException ex) {
throw new JspException("IO Error: " + ex.getMessage());
}
return EVAL_PAGE;
}
/** called at the end of the body of the tag */
public int doAfterBody() throws JspException {
return SKIP_BODY;
}
/** pageContext property setter method */
public void setPageContext(PageContext aPageContext) {
pageContext = aPageContext;
}
/** parent property setter method */
public void setParent(Tag aParent) {
parent = aParent;
}
/** parent property getter method */
public Tag getParent() {
return parent;
}
/** clean up tasks */
public void release() {
}
/** current page context */
private PageContext pageContext;
/** parent tag */
private Tag parent;
}
webshop\sqltags\OutputTag.java
package webshop.sqltags;
import java.io.*;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
/** JSP tag for displaying query results. Used as a subtag
* of <code>SQLQueryTag</code>. Renders its body once for each
* row of a result set.
*
167
* @see SQLQueryTag
* @see OutputStartTag
* @see OutputEndTag
* @see EmptyTag
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
* @version 1.0
*/
public class OutputTag extends BodyTagSupport {
/** called at the beginning of the tag */
public int doStartTag() throws JspException {
if (parent == null)
return SKIP_BODY;
if (!(parent instanceof SQLQueryTag))
return SKIP_BODY;
SQLQueryTag myParent = (SQLQueryTag)parent;
if (myParent.getMaxRows() == 0)
return SKIP_BODY;
return EVAL_BODY_TAG;
}
/** called at the end of the tag */
public int doEndTag() throws JspException {
try {
if (bodyContent != null)
bodyContent.writeOut(bodyContent.getEnclosingWriter());
} catch(IOException ex) {
throw new JspException("IO Error: " + ex.getMessage());
}
return EVAL_PAGE;
}
/** called at the end of the body of the tag */
public int doAfterBody() throws JspException {
return SKIP_BODY;
}
/** pageContext property setter method */
public void setPageContext(PageContext aPageContext) {
pageContext = aPageContext;
}
/** parent property setter method */
public void setParent(Tag aParent) {
parent = aParent;
}
/** parent property getter method */
public Tag getParent() {
return parent;
}
/** clean up tasks */
public void release() {
}
/** current page context */
private PageContext pageContext;
/** parent tag */
private Tag parent;
}
webshop\sqltags\OutputEndTag.java
package webshop.sqltags;
import java.io.*;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
/** JSP tag for displaying query results. Used as a subtag
* of <code>SQLQueryTag</code>. Renders its body once, at the end
* of a result set.
*
* @see OutputStartTag
* @see SQLQueryTag
168
* @see OutputTag
* @see EmptyTag
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
* @version 1.0
*/
public class OutputEndTag extends BodyTagSupport {
/** called at the beginning of the tag */
public int doStartTag() throws JspException {
if (parent == null)
return SKIP_BODY;
if (!(parent instanceof SQLQueryTag))
return SKIP_BODY;
SQLQueryTag myParent = (SQLQueryTag)parent;
if (myParent.getRowCounter()==myParent.getMaxRows()-1 && myParent.getMaxRows() > 0)
return EVAL_BODY_TAG;
else
return SKIP_BODY;
}
/** called at the end of the tag */
public int doEndTag() throws JspException {
try {
if (bodyContent != null)
bodyContent.writeOut(bodyContent.getEnclosingWriter());
} catch(IOException ex) {
throw new JspException("IO Error: " + ex.getMessage());
}
return EVAL_PAGE;
}
/** called at the end of the body of the tag */
public int doAfterBody() throws JspException {
return SKIP_BODY;
}
/** pageContext property setter method */
public void setPageContext(PageContext aPageContext) {
pageContext = aPageContext;
}
/** parent property setter method */
public void setParent(Tag aParent) {
parent = aParent;
}
/** parent property getter method */
public Tag getParent() {
return parent;
}
/** clean up tasks */
public void release() {
}
/** current page context */
private PageContext pageContext;
/** parent tag */
private Tag parent;
}
webshop\sqltags\EmptyTag.java
package webshop.sqltags;
import java.io.*;
import java.util.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
/** JSP tag for displaying query results. Used as a subtag
* of <code>SQLQueryTag</code>. Renders its body once, at the end
* of a result set.
*
* @see OutputStartTag
* @see OutputEndTag
* @see OutputTag
169
* @see SQLQueryTag
* @author Branko Milosavljevic mbranko@uns.ns.ac.yu
* @version 1.0
*/
public class EmptyTag extends BodyTagSupport {
/** called at the beginning of the tag */
public int doStartTag() throws JspException {
if (parent == null)
return SKIP_BODY;
if (!(parent instanceof SQLQueryTag))
return SKIP_BODY;
SQLQueryTag myParent = (SQLQueryTag)parent;
if (myParent.getMaxRows() == 0)
return EVAL_BODY_TAG;
else
return SKIP_BODY;
}
/** called at the end of the tag */
public int doEndTag() throws JspException {
try {
if (bodyContent != null)
bodyContent.writeOut(bodyContent.getEnclosingWriter());
} catch(IOException ex) {
throw new JspException("IO Error: " + ex.getMessage());
}
return EVAL_PAGE;
}
/** called at the end of the body of the tag */
public int doAfterBody() throws JspException {
return SKIP_BODY;
}
/** pageContext property setter method */
public void setPageContext(PageContext aPageContext) {
pageContext = aPageContext;
}
/** parent property setter method */
public void setParent(Tag aParent) {
parent = aParent;
}
/** parent property getter method */
public Tag getParent() {
return parent;
}
/** clean up tasks */
public void release() {
}
/** current page context */
private PageContext pageContext;
/** parent tag */
private Tag parent;
}
170