topici avansate în proiectarea aplicaţiilor pentru internet · topici avansate în proiectarea...
Post on 24-Oct-2019
4 Views
Preview:
TRANSCRIPT
Topici avansate în proiectarea aplicaţiilor pentru Internet
1. Internetul si World Wide Web
Internetul a fost descris ca „o colecţie largă de reţele“ sau ca o „reţea de reţele“. Deşi ambele definiţii
sunt corecte, niciuna nu surprinde Internetul în totalitatea sa. Pe lîngă instrumentul care este această
imensă conexiune, Internetul înseamnă şi mulţimea comunităţilor celor ce îl folosesc, fiecare în
scopuri diferite:
comunitatea academică utilizează Internetul ca pe cel mai mare, complet şi totodată complex
instrument de învăţare (educaţional);
comunitatea ştiinţifică utilizează Internetul ca pe un instrument de cercetare şi colaborare;
comunitatea economică utilizează Internetul ca pe un mediu de derulare al afacerilor.
1.1 World Wide Web
World Wide Web (WWW sau W3) este o reţea de resurse informaţionale de o extraordinar de
mare diversitate în ceea ce priveşte conţinutul. Este un sistem interactiv hipermedia (adică un
sistem ce conţine şi suportă patru categorii importante de tipuri de informaţie: texte, imagini,
sunete/audio şi imagini video în mişcare) construit peste Internet.
Pentru a face aceste resurse disponibile (utilizabile) unei audienţe cît mai largi, Web-ul se
sprijină pe 3 mecanisme fundamentale:
1. O schemă uniformă de denumire (de stabilire a numelor, naming scheme) pentru a localiza
resursele în Web (de exemplu URI).
2. Protocoale pentru accesarea resurselor astfel denumite în Web (de exemplu HTTP)
3. Hypertextul pentru navigarea comodă de la o resursă la alta (între resurse).
1.2 Resursele World Wide Web
O resursă poate fi un program, un calculator, un document, o bază de date, un serviciu - nu prea
are importanţă, atît timp cît poate fi referită în mod corect şi fără echivoc. Pentru referirea la o
resursă din Internet, se foloseşte termenul generic URI (Universal Resource Identifier).
URI poate specifica:
o locaţie, caz în care se vorbeşte de un URL (Universal Resource Locator);
un nume, caz în care se vorbeşte de un URN (Universal Resource Name).
Unei resurse i se aplică o metodă - iar pentru a specifica ce metodă se doreşte, ce date sau
parametrii suplimentari o completează pe aceasta, se face uz de mesaje.
Paradigma pe care se bazează protocolul este cea de cerere/răspuns. Cererea este emisă de
un client; acesta stabileşte o conexiune cu un server şi îi trimite acestuia o cerere, sub forma
unei metode. Metoda se referă la o anumită resursă, identificată via URI; mai trebuie
adăugate versiunea de protocol utilizată şi un mesaj de tip MIME care să conţină parametrii
metodei, informaţii relative la client şi un eventual “conţinut” suplimentar. Serverul vă
răspunde cu o linie de stare, incluzînd versiunea de protocol utilizată şi un cod de succes sau
eroare, la care se adaugă un mesaj de tip MIME conţinînd informaţii relative la server şi
eventual un “conţinut” suplimentar.
Acest posibil conţinut suplimentar este de regulă o entitate - o reprezentare particulară a
unor date necesare în cerere sau în răspuns, şi este structurat într-un antet (header)
conţinînd metainformaţii relative la date (o descriere a felului în care trebuie citite datele) şi
datele propriu-zise, care formează corpul entităţii
Adresarea unei resurse via http se face prin construcţii de forma:
http://adresa_host_in_retea[:port]/cale/subcalel/.../subcalen/nume_document
Exemplu:
http://guaraldi.cs.colostate.edu:2000/cgi-bin/savvyfrontend?KW=cuvînt_cheie &
classic=on & tl=x & Boolean=AND & Hits=10 & Mode=MakePlan & df=normal & AutoStep=on.
În HTML adresarea URI se foloseşte pentru:
crearea unei legături spre un alt document sau spre o altă resursă (a se vedea elementele A şi
LINK)
crearea unei legături spre un stil de pagină (style-sheet) extern sau spre un script aflat într-un
fişier sursă extern (a se vedea elementele LINK şi SCRIPT);
Includerea într-o pagină a unei imagini, a unui obiect sau a unui applet (a se vedea
elementele IMG, OBJECT, APPLET şi INPUT);
crearea unei imagini senzitive (a se vedea elementele MAP şi AREA);
transmiterea unui formular interactiv (a se vedea elementul FORM);
crearea unui document cu frame-uri (a se vedea elementele FRAME şi IFRAME);
citarea unei referinţe externe (a se vedea elementele Q, BLOCKQUOTE, INS şi DEL);
referirea unor conveţii de metadate care descriu un document (a se vedea elementul HEAD).
În cazul cel mai simplu, legătura dintre client şi server se realizează prin intermediul unei singure
conexiuni. De foarte multe ori însă, este posibil să existe mai mulţi intermediari în conexiune.
Aceştia pot fi de trei feluri:
proxy,
gateway,
tunnel.
1.3 Protocolul http
Este un protocol la nivel aplicaţie destinat sistemelor de informare distribuite, “colaborative“, de
genul hypermedia. Apărut ca protocol de bază pentru WWW încă din 1990, a cunoscut o serie de
transformări, o versiune “finală” neexistînd nici în prezent.
Numele este acronimul pentru HyperText Transfer Protocol.
Aplicaţiile care folosesc protocolul - cei doi parteneri în discuţie, cele două capete ale unei
conexiuni - sînt entităţi abstracte din punct de vedere al protocolului. Cererile formulate în
protocolul HTTP se referă la informaţii care se pot afla stocate în diverse baze de date, în diverse
formate, pe diverse calculatoare. Cum anume se traduc în cereri “concrete” date diferite, este o
problemă care depăşeşte protocolul: sarcina lui este doar să fixeze regulile care trebuie
respectate de cele două aplicaţii participante la un moment dat în comunicare pentru ca să se
poată înţelege fără nici un fel de risc de interpretare eronată a unei cereri sau a unui răspuns.
Atunci cînd se transferă „ceva“ utilizînd WWW se specifică o resursă: serverul căruia am vrea să-i
adresăm cererea, ce conţine aceasta, cu ce protocol lucrăm.
Pentru ca această cerere să ajungă la server trebuie să trimitem un mesaj care să conţină şi
resursa specificată mai sus. Mesajul va conţine un şir de caractere de forma:
GET specificare_resursă HTTP/1.1 CRLF
Forma generală a unui mesaj de cerere este:
Metodă resursă versiune_protocol CRLF
Metodele sunt de fapt operaţiile care pot fi aplicate obiectelor constituite de resursele din reţea,
în accepţiunea protocolului HTTP. Metoda va trebui să fie totdeauna primul element dintr-o linie
de cerere. Metodele prevăzute în versiunea 1.1 sînt următoarele:
OPTIONS, GET, HEAD, POST, PUT, PATCH, COPY, MOVE, DELETE, LINK, UNLINK, TRACE,
WRAPPED.
OPTIONS semnifică o cerere relativă la informaţiile ce definesc opţiunile de comunicare
disponibile pe conexiunea către URI-ul specificat în cerere. Metoda permite determinarea
opţiunilor şi/sau posibilităţilor unui server, fără să determine o acţiune din partea resursei
adresate.
Şi metoda are nevoie de parametri, nu numai resursa, iar în HTTP termenul consacrat pentru
parametrii metodelor este “header field” sau “antet de cîmp”. Definite în cadrul protocolului
pentru fiecare metodă, antetele de cîmp pot avea valori care la rîndul lor sînt definite (dar nu
limitate, extensiile fiind în principiu totdeauna posibile).
Exemplu:
O cerere de tipul
OPTIONS www.xxx.ro HTTP1/1 CRLF Accept: audio/*; q=0.2, audio/basic reprezintă o cerere
de definire a opţiunilor către serverul www.xxx.ro, în care clientul solicitant spune că preferă
audio/basic, dar acceptă orice tip pentru date audio în cazul în care calitatea reprezentării nu
scade sub 20%.
GET este una dintre cele mai importante metode şi singura care era disponibilă în prima versiune
a protocolului, HTTP/0.9. GET este metoda care “aduce” ceva de la resursă; mai concret, dacă
resursa este un proces care produce date (o căutare de pildă), răspunsul la metoda GET va fi o
entitate care să cuprindă acele date. Răspunsul este unul singur: aceasta este o caracteristică de
bază a protocolului. Chiar dacă volumul de date care trebuie incluse în răspuns este mare, nu se
face o fracţionare în bucăţele mai mici, care să permită transferul mai uşor al răspunsului.
Există totuşi două posibilităţi de a micşora volumul de date care să circule pe reţea în urma
elaborării unui răspuns; o condiţionare de genul “dacă s-a schimbat ceva” şi posibilitatea de a
prelua numai o parte din acesta. De exemplu, o cerere de genul:
GET www.x.ro/?cerere HTTP/1.1 If-Modified-Since: Wed, 24 Mar 1999 1:00:00 GMT
va aduce ceea ce s-a cerut numai dacă s-a modificat ceva după data şi ora specificate în
parametrii metodei.
HEAD este o metodă similară cu GET, folosită în principiu pentru testarea validităţii şi/sau
accesibilităţii unei resurse, sau pentru a afla dacă s-a schimbat ceva. Sintaxa este similară
metodei GET; spre deosebire de GET însă, datele eventual produse de resursă în urma cererii nu
sînt transmise; doar caracteristicile acestora, şi un cod de succes sau eroare. Ceva de genul “dacă
ţi-aş cere să execuţi cererea mea, ce mi-ai răspunde?”.
POST este metoda prin care resursei specificate în cerere i se cere să îşi subordoneze datele
incluse în entitatea care trebuie să însoţească cererea. Cu POST se poate adăuga un fişier unui
anumit director, se poate trimite un mesaj prin poştă electronică, se poate adăuga un mesaj unui
grup de ştiri, se pot adăuga date unei baze de date existente, etc. Metoda POST este generală;
care sînt procesele pe care un anumit server le acceptă sau cunoaşte îi sînt strict specifice.
1.4 HTML
HTML este un limbaj care a fost dezvoltat iniţial de oamenii de ştiinţă ca o unealtă pentru
partajarea documentelor (rapoarte de cercetare, documentaţii, etc) în întreaga comunitate
ştiinţifică internaţională care utiliza (şi utilizează) Internetul. Pentru a publica informaţii care să
fie distribuite global în Internet este necesar un limbaj universal de descriere a acestora, limbaj
care să fie potenţial înţeles de toate computerele din Internet. Limbajul folosit de World Wide
Web este HTML.
HTML se aseamănă cu modalităţile de formatare a textelor de la un procesor de texte uzual
diferenţa constand în faptul că un document HTML este un document ce conţine informaţie în
format „text-pur“ (numai caractere ASCII).
Limbajul HTML oferă posibilitatea:
să publice documente cu headere, texte, tabele, liste, fotografii, etc...
să regăsească on-line informaţii prin intermediul hiperlink-urilor accesate printr-un simplu
click de mouse;
să proiecteze formulare pentru realizarea tranzacţiilor cu servere aflate la distanţă, pentru
căutari de informaţie sau pentru activităţi specifice comerţului;
să includă foi de calcul tabelar, clipuri video, sunete şi alte aplicaţii direct în documente.
HTML are patru caracteristici principale:
Foloseşte un „marcaj“ descriptiv pentru a indica diversele acţiuni („instrucţiuni“) ce trebuie
executate.
Defineşte structuri de documente ierarhice şi (hyper)legături intra- şi inter-documente.
Limbajul HTML este guvernat de o descriere formală.
Atît specificaţiile limbajului cît şi limbajul însuşi pot fi „citite“ (interpretate) şi de om dar şi de
computer.
2. XML EXtensible Markup Language
2.1 Introducere
Extensible Markup Language, abreviat XML, descie o clasă de obiecte numite documente XML şi
descrie parţial comportamentul unor programe de computer care le procesează. XML este o aplicaţie
profil sau o formă restrictivă a SGML-ului, Standard Generalized Markup Language [ISO8879]. Prin
construcţie, documentele XML se conformează documentelor SGML.
Documentele XML sunt realizate din unităţi de stocare numite entităţi, ce conţin date parsate sau neparsate. Datele parsate sunt realizate din caractere, unele dintre ele formând date caracter iar altele ca marcaje. Marcajele codifică o descriere a schemei de stocare a documentului şi structura logică. XML furnizează un mecanism pentru a impune constrîngeri asupra schemei de stocare şi a structurii logice.
Un modul software numit procesor XML este utilizat pentru a citi documente XML şi pentru a da acces la structura şi conţinutul lor. Se consideră că un procesor XML îşi face munca în spatele unui alt
modul, numit aplicaţie. Această specificaţie descrie comportamentul cerut unui procesor XML în termeni ce spun cum trebuie să citească datele XML şi ce informaţii trebuie să-i furnizeze aplicaţiei.
În timp ce HTML este un limbaj markup singular, proiectat pentru satisfacerea unor anumite cerinţe,
XML este o adevărată familie de limbaje markup: pornind de la XML utilizatorul poate defini alte
limbaje markup; aceasta înseamnă că aproape orice tip de date poate fi definit cu uşurinţă în XML. În
aceste condiţii pe lângă un mediu de comunicaţie universal (Internetul), o interfaţă utilizator
universală (browser-ul) şi un limbaj de programare universal (Java) avem acum şi un format de date
universal - XML.
XML este universal nu doar prin spectrul său de aplicaţii, dar şi prin uşurinţa utilizării. Faptul că este
bazat pe text îl face uşor de utilizat pentru crearea de aplicaţii şi de unelte pentru dezvoltarea
aplicaţiilor; este de asemenea un standard deschis, motiv pentru care nu necesită licenţă şi poate fi
folosit pe toate platformele hardware/software. Ceea ce îl face de asemenea universal este puterea
sa. Datele sunt transmise şi stocate în computere în diferite moduri: iniţial ele erau stocate în fişiere
cu lungime fixă sau formate delimitate şi apoi s-au mutat în baze de date şi adesea în formate binare
complexe. XML este un format de date structurate, care permite stocarea datelor complexe,
indiferent dacă iniţial erau de tip text, binare sau orientate-obiect.
2.2 Scurt Istoric
XML (eXtensible Markup Language), descendent al SGML (Standard Generalized Markup Language)
este un meta-limbaj utilizat în activitatea de marcare structurală a documentelor, a cărei specificaţie
a fost dezvoltată începând cu 1996 în cadrul Consorţiului World Wide Web (W3C), de un grup de
cercetare condus de Jon Bosak de la Sun Microsystems, la care au aderat ulterior o serie de grupuri
de experţi din comunităţile academice (Text Encoding Initiative, NCSA, James Clark) şi industriale
(SUN, IBM, Netscape, Oracle, Adobe etc.). Prima versiune de XML a fost standardizată în 1998, fiind
revizuită recent, în luna octombrie 2000.
XML este o metodă de descriere a informaţiei astfel încât atât computerele, cât mai ales oamenii să o
poată înţelege.
Scopurile limbajului au fost cele legate de utilizarea lui în Internet, suportând o varietate de aplicaţii,
dar păstrând compatibilitatea cu SGML. În fapt, XML este compus dintr-o familie de limbaje menite a
adapta conceptele curente de publicare a documentelor la Internet. XML este compus din:
XML (eXtensible Markup Language) - subset al specificaţiei SGML, conceput pentruimplementare mai uşoară, având în ultima perioadă o dezvoltare extraordinară;
XLL (eXtensible Linking Language) - set al mecanismelor hipertext bazate pe HyTime şi Text Encoding Initiative, concretizat în două componente majore:
XLink - conceput pentru descrierea legăturilor dintre obiectele Internet;
XPointer - compus dintr-o serie de termeni de localizare relativi la alte locaţii; XSL (eXtensible Stylesheet Language) - limbaj standard al foilor de stil, ca subset al DSSSL,
permiţând ataşarea de semantici elementelor XML şi transformarea documentelor XML în alt tip de documente (XML, HTML, MathML etc.);
XUA (XML User Agent) - direcţie de standardizare a navigatoarelor XML.
Scopurile avute în vedere pentru XML sunt:
1. XML trebuie să fie simplu de utilizat pe Internet. 2. XML trebuie să suporte o mare verietate de aplicaţii. 3. XML trebuie să fie compatibil cu SGML. 4. Trebuie să fie uşor să fie scrise programe ce vor procesa documente XML. 5. Numărul facilităţiilor opţionale din XML sunt reduse la minimum, ideal, la zero. 6. Documentele XML trebuie să fie citibile de către utilizatori şi clare într-un mod rezonabile. 7. Designul XML ar trebui să fie pregătită rapid. 8. Designul XML trebuie să fie formal şi concis. 9. Documentele XML trebuie să fie uşor de creat. 10. Caracterul lapidar din marcajele XML să fie de o importanţă minimă.
2.3 Cum s-a ajuns la XML?
Deşi tehnologiile XML şi HTML (HyperText Markup Language) sunt descendente ale SGML, sunt
complet diferite. Pentru cei care doreau să facă lucruri ce se puteau realiza prin SGML, însă fără a
învăţa întreg limbajul, a fost inventat HTML-ul. HTML-ul este simplu de învăţat şi poate fi utilizat pe
multe platforme, dar deşi este foarte bun în a expune informaţii, HTML-ul nu este la fel de bun în a le
descrie. Şi din acest motiv s-a ajuns la XML: o metodă universală pentru a reprezenta, descrie
informaţii. XML este o tehnologie complementară a HTML-ului, nu o înlocuire a sa.
De exemplu, dacă am considera un fragment de document HTML utilizat pentru a reprezenta
informaţii despre automobile, acesta ar putea fi de forma:
<p><b>Descriere: </b></p>
<p>Producator: BMW</p>
<p>Model: Z3</p>
<p>Culoare: rosu</p>
Din perspectiva unui calculator, nu este nici o indicaţie asupra faptului că informaţia ar fi structurată.
În schimb, considerând aceeaşi informaţie reprezentată prin XML, fragmentul de
document ar fi:
<Descriere>
<Producator>BMW</Producator>
<Model>Z3</Model>
<Culoare>rosu</Culoare>
</Descriere>
În acest caz, privind acest fragment putem spune că Descriere conţine Producator, Model şi Culoare.
Cunoscând structura datelor, putem să facem anumite operaţii asupra lor, să afişăm doar anumite
date, să efectuăm diverse tipuri de căutări etc. De aceea, în multe cazuri, utilizarea XML-ului este
necesară.
Sistemul XML
Un sistem XML este compus din patru părţi generale: documentul XML, definiţia
tipului de document (DTD = Document Type Definition), analizorul XML, şi o parte unică, aplicaţia
XML. În termeni non-tehnici acestea pot fi numite conţinut, definiţia structurii, analizor şi aplicaţia.
Datele pe care le procesează un sistem XML sunt cele puse la dispoziţie de documentul XML în care
sunt descrise informaţiile folosindu-se sintaxa XML. Pentru a se asigura faptul că informaţia este
reprezentată conform sintaxei XML, documentul este rulat de către un procesor XML numit analizor
XML care verifică corectitudinea sintactică şi returnează structura informaţiei în memorie.
Reprezentarea returnată va fi disponibilă aplicaţiei XML.
Dacă dorim ca informaţiile dintr-un document să fie specificate într-un anumit mod, atunci îi vom
ataşa documentului un DTD (Document Type Definition). DTD-ul conţine de fapt un set de reguli prin
care se specifică modul în care dorim să fie structurată informaţia din document.
Dacă se doreşte, analizorul XML poate verifica şi corectitudinea structurală a unui document XML,
prin parcurgerea acestuia vis –à-vis de DTD-ul asociat. Un analizor citeşte informaţiile din documentul
XML şi le face disponibile aplicaţiei XML. Se presupune că toate analizoarele XML ar trebui să aibă
acelaşi comportament, dar în realitate fiecare are propriile sale particularităţi. Alegerea unui anumit
analizor este importantă şi depinde mai ales de limbajul în care va fi scrisă aplicaţia XML. Unele sunt
scrise pentru C++, altele pentru Java, etc. Dintre cele mai cunoscute şi utilizate analizoare putem
enumera:
IBM XML Parser pentru Java DataChannel Parser pentru Java (XJParser) Sun XML Parser pentru Java Expat MSXML (analizorul Microsoft de XML inclus în Internet Explorer 5 şi versiunile ulterioare)
care poate fi utilizat din diverse limbaje (Visual C++, Visual Basic, Visual J++, etc.)
Aplicaţiile XML procesează informaţiile din documentele XML, dar aceasta nu înseamnă că există
limite în ceea ce s-ar putea face cu o aplicaţie. În general, pentru aplicaţii sunt utilizate analizoarele
create de marile companii, deoarece scrierea unui analizor propriu necesită un efort deosebit.
2.4 Documente XML
2.4.1 Structura documentelor XML
Un document XML este format din marcaje (tag-uri) şi date caracter.
Cuvântul marcaj (markup) a fost folosit iniţial pentru a descrie anumite adnotări, note marginale în
cadrul unui text cu intenţia de a indica tehnoredactorului cum trebuie listat un anumit pasaj.
Generalizând, putem defini marcajul drept orice acţiune de a interpreta explicit o porţiune de text.
Un marcaj este un şir de caractere delimitat de caracterele “<” şi “>”. Datele caracter reprezintă
conţinutul marcajelor.
În XML, marcajele nu sunt folosite pentru afişarea datelor conţinute, ci au alte scopuri printre care:
asigură o sintaxă simplă şi standardizată pe care analizoarele XML o pot folosi pentru a utiliza informaţia stocată
asigură o metodă de a descrie structura ierarhică a conţinutului prin împărţirea informaţiei (datele caracter) în părţi numite elemente care mai departe sunt descrise prin atribute. Structura ierarhică a întregului document este pusă în evidenţă prin utilizarea marcajelor.
Un document XML poate fi împărţit în trei secţiuni generale de colecţii de marcaje:
Prolog Declaraţia tipului de document Elementul rădăcină
Fiecare dintre aceste secţiuni poate fi mai departe împărţită în structuri mai detaliate, o reprezentare
generală a structurii a unui document XML putând fi urmărită în următoarea schemă:
Prolog
- Comentarii
- Instrucţiuni de procesare
Declaraţia tipului de document
- Comentarii
DOCUMENT - Instrucţiuni de procesare
XML - Definiţii
- Elemente
- Atribute
- Entităţi
- Notaţii
Element rădăcină
- Comentarii
- Instrucţiuni de procesare
- Elemente
- Atribute
- CDATA, entităţi etc
- PCDATA
- Referinţe la entităţi
- Referinţe la entităţi
- Secţiuni CDATA
2.4.2 Documente bine formatate şi documente valide
Documentele bine formatate sunt documentele corecte din punct de vedere sintactic. Spre deosebire
de HTML, XML este mai strict în ceea ce priveşte sintaxa. Regulile ce trebuie respectate pentru ca un
document să fie bine formatat, sunt:
spre deosebire de HTML şi SGML, XML este case sensitive. De aceea trebuie să fim atenţi când scriem marcajele, astfel de greşeli fiind destul de greu de detectat, mai ales când nu se foloseşte un editor de XML.
fiecare tag deschis trebuie închis, în caz contrar analizorul XML va semnala eroare. incluziunile şi excluziunile nu sunt permise. Primul tag deschis trebuie să fie ultimul care este
închis. Tag-urile trebuie închise exact în ordinea inversă a deschiderii lor, altfel va fi semnalată eroare.
nu sunt acceptate elementele vide. Elementele vide au o sintaxă modificată:
<element />
Există două categorii de documente XML: bine formatate şi documente valide. Un document XML valid este acel a cărei structură este corectă vis-à-vis de DTD-ul ataşat.(pot fi valide doar documentele care au ataşat un DTD). Un analizor efectuează două nivele de verificare a unui document XML. După verificarea corectitudinii sintactice, analizorul poate verifica dacă conţinutul documentului este conform setului de reguli din DTD. Dacă conţinutul respectă aceste reguli atunci spunem despre documentul XML că este valid. Procesul de verificare dacă un document este valid sau nu se numeşte validare.
2.4.3 Sintaxa documentelor XML
După cum am mai menţionat, un document XML este format din conţinut (date caracter) încapsulat
în marcaje.
Există şapte tipuri de marcaje ce pot fi folosite într-un document XML:
Începutul unui element şi tag-ul de sfârşit Atribute Comentarii Referinţe la entităţi Instrucţiuni de procesare Secţiuni CDATA Declaraţia tipului de document
2.5 Definirea tipului de document (DTD)
Ca şi în cazul SGML, tipul unui document XML reprezintă modul în care vor fi specificate informaţiile
în acel document. Acest lucru se realizează prin intermediul DTD-ului (Document Type Definition), iar
acest DTD poate fi intern sau extern.
Documentele XML pot avea un DTD sau DTD-ul poate lipsi. Dacă există un DTD intern sau extern,
atunci utilizatorul trebuie să se conformeze acestui DTD, iar în cazul în care lipsesc ambele, atunci
utilizatorul poate construi documente respectând un număr minim de constrângeri.
DTD-ul intern se declară imediat după declaraţia XML sau, dacă această declaraţie nu există, el va fi
primul element exceptând comentariile, spaţiile de nume sau instrucţiunile de procesare. DTD-ul
intern se declară utilizând cuvântul cheie DOCTYPE în modul următor:
<!DOCTYPE element_rădăcină *
declaraţie de elemente, atribute, entităţi, instrucţiuni de procesare, notaţii
]>
DTD-ul extern este definit într-un alt fişier text care trebuie să se afle la o adresă specificată.
Declaraţia este asemănătoare, dar intervin cuvintele cheie SYSTEM sau PUBLIC:
<!DOCTYPE carte
SYSTEM "http:/xxx.com/carte.dtd">
<!DOCTYPE carte
PUBLIC "carte" "http:/xxx.com/carte.dtd">
Fişierul "carte.dtd" aflat la adresa specificată în declaraţie conţine definiţiile elementelor care vor
apare în document. Elementul carte trebuie să apară în DTD-ul specificat. Declaraţia SYSTEM
specifică faptul că DTD-ul se află la acea adresă, iar PUBLIC înseamnă că va fi generat un fişier care va
conţine acel DTD.
2.6 Spaţii de nume
Pot apare confuzii atunci când se folosesc date din diverse surse elemente, care pot avea elemente
cu acelaşi nume, dar cu semnificaţii diferite, iar pentru evitarea acestor ambiguităţi sunt folosite
spaţiile de nume, astfel încât numele de elemente vor fi identificate în mod unic, iar validarea se va
realiza fără probleme.
Necesitatea folosirii spaţiilor de nume se va vedea din exemplul următor:
<carte autor = “Eminescu”
titlu=“Poezii”
editura=“Nemira” >
</carte>
şi putem avea într-un alt document:
<carte>
<autor>Cosbuc</autor>
<titlu>Poezii</poezii>
<editura>Nemira</editura>
</carte>
Documentul care le utilizează pe precedentele şi foloseşte spaţii de nume pentru evitarea
ambiguităţilor ar putea fi următorul:
<rezultat
xmlns:x=“http//xxx.com/doc1.dtd”
xmlns:y=“http://yyy.com/doc2.dtd”>
<x:carte>
<x:autor>Cosbuc<-x:autor>
<x:titlu>Poezii<-x:titlu>
<x:editura>Nemira<-x:editura>
</x:carte>
<y:carte y:autor=“Eminescu”
y:titlu=“Poezii”
y:editura=“Nemira”>
</y:carte>
</rezultat>
xmlns face parte dintre cuvintele rezervate ale XML-ului, folosit pentru declararea spaţiilor de nume,
iar valoarea atributului său trebuie să fie un URL. Spaţiile de nume se pot declara explicit (ca prefix
sau substitut pentru numele complet al unui spaţiu de nume – o declarare explicită este cea a
spaţiului de nume din exemplul anterior), sau implicit (un spaţiu de nume pentru toate elementele
domeniului lui de vizibilitate).
Exemplu de declarare implicită:
<carte xmlns=”http://xxx.com/schema”>
<titlu>Poezii</titlu>
<autor>Eminescu</autor>
</carte>
2.7 XML Schema
O schemă reprezintă o specificaţie formală a gramaticii asociate unui limbaj definit în XML, utilă
pentru validarea documentelor scrise în acel limbaj. Desigur, fiecărui document îi puteam asocia, în
mod explicit, un DTD folosit pentru validarea acestuia, reminiscenţă de la SGML.
Schemele utilizează sintaxa XML şi sunt mai natural de definit decât DTD-urile, specificaţiile fiind încă
în faza preliminară, bazate pe Document Content Description (DCD) şi XML-Data, ambele în lucru la
Consorţiul Web. În prezent, schemele XML sunt suportate în formă limitată de Internet Explorer 5 şi
Netscape 6.
2.7.1 Definirea elementelor şi atributelor
Ca şi la DTD, cea mai importantă parte dintr-o schemă XML o reprezintă specificarea elementelor şi
atributelor care pot compune un document, incluzând tipul şi ordinea lor de apariţie. Elementele şi
atributele sunt definite în XML Schema prin tag-urile <ElementType> şi, respectiv, <AttributeType>,
iar instanţele lor prin <element> şi <attribute>, respectiv. Această abordare este similară declaraţiei
typedef din limbajul C.
Un exemplu:
<?xml version="1.0"?>
<Schema name="mySchema" xmlns="schemas-microsoft-com:xml-data">
<ElementType name="nume" content="textOnly" />
<ElementType name="prenume" content="textOnly" />
<ElementType name="adresa" content="textOnly" />
<ElementType name="persoana" order="many">
<element type="nume" maxOccurs="1" />
<element type="prenume" maxOccurs="1" />
<element type="adresa" />
<AttributeType name="id" required="yes" />
<attribute type="id" />
</ElementType>
</Schema>
Schema specificată în acest exemplu va fi referită prin numele "mySchema" stabilit prin intermediul
atributului name al tag-ului <Schema>. Fiecare schemă are asociat un spaţiu de nume specificat prin
xmlns.
Atributul order din definiţia unui tip de element dictează regulile de apariţie a elementelor în cadrul
unui element părinte, putând avea valorile:
seq - sub-elementele apar în secvenţa specificată în cadrul schemei;
many - sub-elementele pot apare în orice ordine, în orice cantitate;
one - un singur sub-element din lista celor specificate poate fi utilizat;
empty - nu se permite apariţia nici unui sub-element (elementul va fi vid).
Fiecărui <ElementType> îi putem asocia atributul content care dictează tipul conţinutului acestuia:
textOnly - numai text, fără alte sub-elemente;
eltOnly - doar sub-elemente;
mixed - conţinut eterogen, şi sub-elemente, şi text.
Implicit, pentru content="eltOnly" se consideră că order este setat pe seq, iar pentru
content="mixed" atributul order are valoarea many.
2.7.2 XML Schema vs. DTD
Între XML Schema şi DTD există următoarele deosebiri principale:
Schemele şi DTD-urile utilizează moduri diferite de specificare a conţinutului documentelor XML. În cadrul unui model XML Schema deschis, un element poate include definiţii <ElementType> şi <AttributeType> din alte scheme. Modelul XML Schema închis şi DTD sunt similare.
În XML Schema tipurile posibile de date pentru elemente şi atribute pot fi variate şi complexe, la specificaţia XML-Data încă lucrându-se în prezent. În cazul DTD, această flexibilitate nu există.
Includerea declaraţiilor de spaţii de nume XML nu poate fi modelată în DTD. Schemele pot fi extinse, pe când DTD-urile prin natura lor sunt fixe.
<ElementType> poate avea un unic element descendent, ceea ce în DTD nu se poate defini în mod strict.
3. XSL EXtensible Stylesheet Language
3.1 Introducere
Aşa cum s-a arătat în capitolele anterioare tehnologia XML este foarte puternică şi utilă. Cu siguranţă
că posibilitatea de a transforma cu uşurinţă un document XML într-un alt format precum HTML,
WAP, text, etc. a contribuit la creşterea popularităţii XML-ului. Există două tehnologii care pot fi
utilizate în acest sens, şi anume:
CSS- Cascading Style Sheet;
XSL Extensible Stylesheet Language.
Dintre aceste două tehnologii XSL este mai puternică şi mai flexibilă în comparaţie cu utilizarea CSS şi,
în acelaşi timp, este mai apropiată de conceptele XML, fiind practic un limbaj bazat chiar pe XML, şi
care utilizează stylesheet-uri pentru a transforma documentului de intrare.
În practică XSL documentul de intrare este numit arbore sursă (arbore de intrare), iar documentul de
ieşire arbore de ieşire.
În cadrul XSL sunt definite două limbaje:
XSLT – limbaj pentru transformări; XSL Formatting Objects – utilizat pentru transformarea documentelor XML în documente
cu format binar precum PDF sau Microsoft Word.
Deşi sunt două limbaje al căror scop este diferit, acestea pot fi folosite împreună în vederea
transformării documentelor XML. Trebuie menţionat totuşi că motoarele XSLT nu suportă obligatoriu
şi XSL Formatting Objects, XSLT fiind practic de sine stătător.
Există trei modalităţi (dintre care două mai importante) în care un document XML poate fi
transformat într-un alt tip de document prin aplicarea unui stylesheet XSLT:
1. Documentul XML şi stylesheet-ul asociat sunt transmise aplicaţiei client (browser-ului) căruia îi
revine sarcina de a realiza efectiv transformarea în conformitate cu informaţia din stylesheet-ul XSLT.
În acste condiţii încărcarea serverului scade, dar borwser-ul trebuie să permită procesarea
documentelor XML;
2. Aplicarea stylesheet-ului XSLT se face chiar pe server, documentul rezultat (uzual în format HTML)
fiind transmis clientului. Se pot realiza astfel procesări în funcţie de natura clientului;
3. Cea de a treia posibilitate este extrem de puţin utilizată şi se referă la transformarea documentului
XML cu ajutorul unei aplicaţii externe şi plasarea pe server a documentului rezultat (HTML), urmând
ca acesta să fie transmis clientului.
Elementul central al tehnologiei XSLT este template-ul: <xsl:template>. În cadrul acestuia se regăsesc
două elemente importante:
atributul match – specifică o cale în arborele de intrare;
conţinutul – implementează modul în care se realizează transformarea. Forma generală a unui template este:
<xsl:tempate match="element_XPath">
...
<xsl:tempate>
Asocierea unui document XML cu un stylesheet XSLT se realizează în cadrul documentului XML cu
ajutorul instrucţiunii de procesare <?xml-stylesheet>:
<?xml-stylesheet href=”stylesheet/Login” type=”text/xsl” />
Argumentul href specifică numele stylesheet-ului XSLT şi dacă este cazul şi calea către acesta.
3.2. Xpath
Pentru a sistematiza modul în care poate fi accesat un nod dintr-un document XML consorţiul W3C a
elaborat specificaţia pentru limbajul XPath. Trebuie menţionat că în cadrul XPath există noţiunea de
root al documentului (document root), noţiune diferită de cea definită în cadrul capitolului 2. Astfel,
se face o distincţie foarte clară între root-ul documentului şi elementul root al documentului XML.
Root-ul documentului, prin prisma XPath, este de fapt rădacina arborelui de elemente reale - definite
în cadrul documentului.
Root-ul documentului este utilizat la nivel conceptual, neavând un element corespondent în cadrul
elementelor documentului XML, şi este reprezentat prin caracterul “/”.
Dacă se doreşte procesarea nodului <mail-messages> din exemplul de mai sus, atunci trebuie
utilizată o expresie XPath de tipul "/mail-messages". Expresiile XPath sunt interpretate de la stânga la
dreapta, iar expresia anterioară poate fi înţeleasă ca: "pornind de la root-ul documentului selectează
elemetul <mail-messages> care este fiu al acestuia (root-ului)".
Omiterea caracterului "/" din expresia XPath anterioară schimbă radical înţelesul acesteia, caz în care
"sunt selectate toate elementele <mail-messages> care sunt fii ai nodului curent". În cazul
elementelor XPath mai complexe, elementele constituente sunt separate prin caracterul "/", care aşa
cum se poate observa are o dublă semnificaţie în funcţie de poziţia în care apare în cadrul
elementului XPath.
Un element XPath precum "/mail-messages/messages/message" permite selectarea tuturor
nodurilor <message> care sunt fii ai elementului <messages>, care este fiu al elementului <mail-
messages>, care la rândul său este fiu al root-ului documentului.
În cadrul elementelor XPath este permisă de asemenea şi utilizarea atributelor. Pentru a deosebi un
element XML de un atribut acestea din urmă sunt precedate de caracterul "@".
Elemente XPath care includ condiţii
Există situaţii specifice în care un element XPath trebuie să facă o selecţie mult mai riguroasă a
elementelor selectate şi tratate în cadrul unui template.
Se poate presupune, spre exemplu, că într-un anumit context se doreşte selectarea doar a
elementelor <input> al căror atribut "type" are o valoare diferită de "hidden". Pentru a realiza acest
lucru, elementul care va fi filtrat trebuie să fie urmat de filtrul care urmează să se aplice. Acesta este
alcătuit dintr-o pereche de paranteze drepte (*+) care încadrează, în mod uzual, o condiţie (poate să
fie şi o condiţie compusă).
3.3. Transformări XSLT
XSLT defineşte un vocabular, pentru transformarea documentelor XML, care include tag-uri XML
pentru arbori, noduri, template-uri şi alte elemente necesare pentru ajustarea şi transformarea
documentelor XML într-un alt vocabular markup (sau acelaşi, cu o altă ordine).
3.3.1. Root-ul stylesheet-ului XSLT
Fiecare stylesheet XSLT trebuie să conţină un element root care este <xsl:stylesheet> şi a cărui
sintaxă este prezentată mai jos.
<xsl:stylesheet
version="numarul_versiunii"
xmlns="http://www.w3.org/1999/XSL/Transform"
>
...
</xsl:stylesheet>
În conformitate cu specificaţia XSLT definită de către consorţiul W3C valoarea pentru atributul
version este 1.0. În cadrul elemetului <xsl:stylesheet> urmează a fi definite toate template-urile ce
apar în stylesheet.
3.3.2. Elemente XSLT
Elementele XSLT sunt construcţii care sunt apelate în cadrul template-urilor şi care realizează o serie
de operaţii şi prelucrări pe baza arborelui de intrare.
<xsl:applay-templates>
Elementul <xsl:applay-templates> apare în interiorul elementelor <xsl:template> şi este utilizat la
apelarea altor template-uri; de obicei este apelat template-ul care se potriveşte cel mai bine cu
elementul XPath specificat în cadrul atributului opţional select. Dacă acesta lipseşte va fi utilizat
nodul context ca punct de pornire.
<xsl:value-of>
Elementul <xsl:value-of> este utilizat pentru a copia valoarea unui nod din cadrul documentului XML
în arborele de ieşire. Atributul select specifică ce nod urmează să fie procesat.
<xsl:attribute>
<xsl:attribute> este utilizat pentru a genera dinamic un atribut pentru elementul în cadrul căruia este
utilizat. Numele atributului care urmează să fie adăugat este specificat prin intermediul atributului
name, iar valoarea acestuia trebuie specificată sub formă de nod text în cadrul tag-ului.
<xsl:for-each>
În unele situaţii este necesar ca mai multe elemente din arborele XML de intrare să fie procesate în
acelaşi mod. O alternativă la utilizarea template-urilor o constituie folosirea elementului <xsl:for-
each>. Alegerea uneia dintre cele două opţiuni se face în funcţie de aplicaţie, în majoritatea cazurilor
ambele variante putând constitui soluţia problemei.
<xsl:variable>
Elementul <xsl:variable> permite adăugarea de constante în cadrul stylesheet-urilor. Definirea unei
astfel de constante se poate face în felul următor:
<xsl:variable name=”n”>24</xsl:variable>
Referirea constantei definite anterior se face prin intermediul unui element XPath:
<xsl:value-of select=”$n” />
Valoarea acesteia este fixă şi nu mai poate fi modificată prin atribuiri ulterioare.
Procesarea condiţionată
Limbajul XSL pune la dispoziţia programatorilor două modalităţi de a modifica ieşirea în funcţie de
datele de intrare. Elementul <xsl:if> permite sau nu (în funcţie de îndeplinirea unei condiţii)
transformarea unui fragment din documentul XML şi inserarea rezultatului în cadrul documentului de
ieşire. <xsl:choose> oferă o soluţie mult mai flexibilă atunci când decizia trebuie luată în funcţie de
mai multe condiţii şi care eventual au legătură între ele. Deşi în multe dintre situaţii funcţionalitatea
celor două elemente menţionate poate fi implementată şi prin intermediul template-urilor, în
majoritatea cazurilor utilizarea acestora conduce la soluţii mai simple şi mai clare.
Concluzii
XSL este o componentă importantă a tehnologiei XML, iar cunoaşterea sa reprezintă un element
esenţial în dezvoltarea aplicaţiilor bazate pe XML. Transformarea documentelor XML se realizează cu
ajutorul stylesheet-urilor XSLT care permit procesări complexe oferind dezvoltatorilor o multitudine
de elemente şi funcţii în acest sens.
Prezentarea într-o viziune originală a conceptelor şi elementelor cu adevărat importante a avut în
vedere crearea unei imagini cât mai clare referitoare la tehnologia XSL, tehnologie care are o
importanţă deosebită în procesul de proiectare şi implementare a aplicaţiilor complexe, bazate pe
tehnologia XSP, care utilizează biblioteci de tag-uri pentru implementarea logicii aplicaţiei.
4. Modele de programare
Un nou limbaj de programare nu are şanse să se impună fără să ofere, pe lângă sintaxa propriu-zisă
un set de biblioteci sau o ierarhie de clase coerentă şi cât mai generală. Atunci când limbajul C a fost
prezentat pentru prima dată, împreună cu el a fost prezentată şi biblioteca standard de intrare ieşire.
Limbajul Java nu numai că nu face excepţie de la această regulă dar chiar o extinde cu ambiţia de a
oferi totul. Interfaţa Java pentru programarea aplicaţiilor (API) oferă o ierarhie de clase care include
funcţionalitate pentru lucrul cu mai multe fire de execuţie, lucrul în reţea, crearea interfeţelor
utilizator complexe, grafică, etc.
Există mai multe moduri de a aborda scrierea unui program. Unul dintre acestea este scrierea unui
program care are iniţiativa pe toată perioada rulării. Acest tip de programe execută în permanenţă o
secvenţă de cod, fie şi numai o buclă de aşteptare a cărei condiţie depinde de elemente exterioare
precum ar fi o apăsare de tastă sau sosirea unui pachet de date din reţea.
Alternativa este aceea de a scrie programe care intră în execuţie doar atunci când sunt generate
anumite evenimente în sistem. În clipa în care apar aceste evenimente, programul le analizează şi
execută o secvenţă de cod specifică evenimentului respectiv. După execuţia codului, programul se
opreşte din nou până la apariţia unui nou eveniment.
Aceste două alternative diferite de a aborda scrierea unui program îşi au rădăcinile în moduri diferite
de lucru ale sistemelor de operare şi în moduri diferite de a gândi interfaţa cu utilizatorul.
Java implementează ambele stiluri de programe discutate mai sus. În primul caz, avem o clasă de
pornire care conţine o funcţie publică principală şi care va fi lansată în execuţie la apelarea
programului. În acest caz programul îşi controlează complet execuţia ulterioară. În termenii
limbajului Java, aceasta este o aplicaţie.
În al doilea caz, codul rulează în interiorul unui navigator Internet. Clasa de pornire trebuie să aibă
implementate metode de răspuns la anumite evenimente pe care le generează navigatorul, precum
ar fi iniţializare, pornire, oprire, desenare, etc. Acest al doilea tip de programe Java le vom numi
apleturi.
Distincţia dintre cele două moduri de organizare a codului este destul de vagă, din cauză că cele două
moduri de lucru se pot amesteca în realitate, un obiect aplet putând fi în acelaşi timp lansat ca
aplicaţie independentă şi invers. Totul depinde de metodele care au fost definite în interiorul clasei
de pornire a programului.
4.1 Structura programelor
4.1.1 Pachete de clase
Clasele Java sunt organizate pe pachete. Aceste pachete pot avea nume ierarhice. Numele de
pachete au forma următoare:
[NumePachet.]* NumeComponentăPachet
Numele de pachete şi de componente ale acestora sunt identificatori Java. De obicei, aceste nume
urmează structura de directoare în care sunt memorate clasele compilate. Rădăcina arborelui de
directoare în care sunt memorate clasele este indicată de o variabilă sistem CLASSPATH. În DOS
aceasta se setează în felul următor:
set CLASSPATH=.;c:\java\lib
În Unix se poate seta cu comanda:
CLASSPATH=.:/usr/local/lib/java ; export CLASSPATH
dacă lucraţi cu bash . Din această rădăcină, fiecare pachet are propriul director. În director există
codul binar pentru componentele pachetului respectiv. Dacă pachetul conţine subpachete, atunci
acestea sunt memorate într-un subdirector în interiorul directorului pachetului.
Creatorii Java recomandă folosirea unei reguli unice de numire a pachetelor, astfel încât să nu apară
conflicte. Convenţia recomandată de ei este aceea de a folosi numele domeniului Internet aparţinând
producătorului claselor. Astfel, numele de pachete ar putea arăta ca în:
COM.Microsoft.OLE
COM.Apple.quicktime.v2
şi aşa mai departe.
4.1.2 Importul claselor
Desigur, este nevoie ca o clasă să poată folosi obiecte aparţinând unei alte clase. Pentru aceasta,
definiţia clasei respective trebuie să importe codul binar al celeilalte clase pentru a şti care sunt
variabilele şi metodele clasei respective.
Importul se face cu o instrucţiune specială:
import numeClasă ;
unde numele clasei include şi pachetul din care aceasta face parte. De exemplu:
import java.awt.Graphics;
import java.applet.Applet;
Se poate importa şi un pachet întreg, adică toate clasele aparţinând acelui pachet, printr-o
instrucţiune de forma:
import numePachet.*;
De exemplu:
import java.awt.*;
4.1.3 Fisiere sursa
Codul sursă Java trebuie introdus cu un editor într-un fişier text pe care îl vom numi în continuare
fişier sursă. Un fişier sursă poate să conţină declaraţia mai multor clase şi interfeţe, dar doar una
dintre acestea poate fi declarată publică. Utilizarea celorlalte clase este limitată la fişierul respectiv.
Mai mult, nu putem avea în acelaşi timp o interfaţă publică şi o clasă publică declarate în acelaşi fişier
sursă.
Dacă dorim să înregistrăm codul clasei într-un anumit pachet, putem să includem la începutul
fişierului sursă o declaraţie de forma:
package numePachet;
dacă această declaraţie lipseşte, clasa va fi plasată în pachetul implicit, care nu are nume.
Structura generală a unui fişier sursă este următoarea:
[ DeclaraţiePachet ][ InstrucţiuneImport ]*[ DeclaraţieDeTip ]*
unde declaraţia de tip poate fi o declaraţie de clasă sau de interfaţă.
4.1.4 Compilare şi execuţie
Fişierele sursă Java au obligatoriu extensia .java . Numele lor este identic cu numele clasei sau
interfeţei publice declarate în interior. În urma compilării rezultă fişiere cu nume identice cu numele
claselor dar cu extensia .class indiferent dacă este vorba de o clasă sau o interfaţă. Fişierul .class este
generat în directorul local şi nu direct la locaţia pachetului.
Compilarea se face cu o comandă de forma:
javac FişierSursă .java
Comanda aceasta, ca şi celelalte descrise în acest paragraf este specifică mediului de dezvoltare Java
pus la dispoziţie de Sun, numit JDK (Java Development Kit). În viitor este probabil să apară multe alte
medii de dezvoltare care vor avea propriile lor compilatoare şi interpretoare şi, posibil, propriile linii
de comandă.
La compilare, variabila sistem CLASSPATH trebuie să fie deja setată pentru că însuşi compilatorul Java
actual este scris în Java.
Pentru lansarea în execuţie a unei aplicaţii Java, trebuie să introduceţi comanda:
java NumeClasă
unde numele clasei este numele aplicaţiei care conţine metoda main . Interpretorul va căuta un fişier
cu numele NumeClasă.class şi va încerca să instanţieze clasa respectivă.
Pentru lansarea unui aplet veţi avea nevoie de un document HTML care conţine tagul APPLET şi ca
parametru al acesteia
name=NumeClasă.class
La lansarea unui aplet, clasele care sunt apelate de clasa principală sunt mai întâi căutate pe sistemul
pe care rulează navigatorul. Dacă nu sunt acolo, ele vor fi transferate în reţea. Asta înseamnă că
transferul de cod este relativ mic, trebuie transferat doar codul specific aplicaţiei.
4.2 Fire de executie si sincronizare
O aplicaţie Java rulează în interiorul unui proces al sistemului de operare. Acest proces constă din
segmente de cod şi segmente de date mapate într-un spaţiu virtual de adresare. Fiecare proces
deţine un număr de resurse alocate de către sistemul de operare, cum ar fi fişiere deschise, regiuni
de memorie alocate dinamic, sau fire de execuţie. Toate aceste resurse deţinute de către un proces
sunt eliberate la terminarea procesului de către sistemul de operare.
Un fir de execuţie este unitatea de execuţie a unui proces. Fiecare fir de execuţie are asociate o
secvenţă de instrucţiuni, un set de regişitri CPU şi o stivă. Atenţie, un proces nu execută nici un fel de
instrucţiuni. El este de fapt un spaţiu de adresare comun pentru unul sau mai multe fire de execuţie.
Execuţia instrucţiunilor cade în responsabilitatea firelor de execuţie. În cele ce urmează vom
prescurta uneori denumirea firelor de execuţie, numindu-le pur şi simplu fire .
În cazul aplicaţiilor Java interpretate, procesul deţine în principal codul interpretorului iar codul binar
Java este tratat ca o zonă de date de către interpretor. Dar, chiar şi în această situaţie, o aplicaţie
Java poate avea mai multe fire de execuţie, create de către interpretor şi care execută, seturi
distincte de instrucţiuni binare Java.
Fiecare dintre aceste fire de execuţie poate rula în paralel pe un procesor separat dacă maşina pe
care rulează aplicaţia este o maşină cu mai multe procesoare. Pe maşinile monoprocesor, senzaţia de
execuţie în paralel a firelor de execuţie este creată prin rotirea acestora pe rând la controlul unităţii
centrale, câte o cuantă de timp fiecare. Algoritmul de rotire al firelor de execuţie este de tip round-
robin.
Mediul de execuţie Java execută propriul său control asupra firelor de execuţie. Algoritmul pentru
planificarea firelor de execuţie, priorităţile şi stările în care se pot afla acestea sunt specifice
aplicaţiilor Java şi implementate identic pe toate platformele pe care a fost portat mediul de execuţie
Java. Totuşi, acest mediu ştie să profite de resursele sistemului pe care lucrează. Dacă sistemul gazdă
lucrează cu mai multe procesoare, Java va folosi toate aceste procesoare pentru a-şi planifica firele
de execuţie. Dacă sistemul oferă multitasking preemptiv, multitaskingul Java va fi de asemenea
preemptiv, etc.
În cazul maşinilor multiprocesor, mediul de execuţie Java şi sistemul de operare sunt responsabile cu
repartizarea firelor de execuţie pe un procesor sau altul. Pentru programator, acest mecanism este
complet transparent, neexistând nici o diferenţă între scrierea unei aplicaţii cu mai multe fire pentru
o maşină cu un singur procesor sau cu mai multe. Desigur, există însă diferenţe în cazul scrierii
aplicaţiilor pe mai multe fire de execuţie faţă de acelea cu un singur fir de execuţie, diferenţe care
provin în principal din cauza necesităţii de sincronizare între firele de execuţie aparţinând aceluiaşi
proces.
Sincronizarea firelor de execuţie înseamnă că acestea se aşteaptă unul pe celălalt pentru
completarea anumitor operaţii care nu se pot executa în paralel sau care trebuie executate într-o
anumită ordine. Java oferă şi în acest caz mecanismele sale proprii de sincronizare, extrem de uşor de
utilizat şi înglobate în chiar sintaxa de bază a limbajului.
La lansarea în execuţie a unei aplicaţii Java este creat automat şi un prim fir de execuţie, numit firul
principal. Acesta poate ulterior să creeze alte fire de execuţie care la rândul lor pot crea alte fire, şi
aşa mai departe. Firele de execuţie dintr-o aplicaţie Java pot fi grupate în grupuri pentru a fi
manipulate în comun.
În afară de firele normale de execuţie, Java oferă şi fire de execuţie cu prioritate mică care lucrează în
fundalul aplicaţiei atunci când nici un alt fir de execuţie nu poate fi rulat. Aceste fire de fundal se
numesc demoni şi execută operaţii costisitoare în timp şi independente de celelalte fire de execuţie.
De exemplu, în Java colectorul de gunoaie lucrează pe un fir de execuţie separat, cu proprietăţi de
demon. În acelaşi fel poate fi gândit un fir de execuţie care execută operaţii de încărcare a unor
imagini din reţea.
O aplicaţie Java se termină atunci când se termină toate firele de execuţie din interiorul ei sau când
nu mai există decât fire demon. Terminarea firului principal de execuţie nu duce la terminarea
automată a aplicaţiei.
4.2.1 Crearea firelor de execuţie
Există două căi de definire de noi fire de execuţie: derivarea din clasa Thread a noi clase şi
implementarea într-o clasă a interfeţei Runnable .
În primul caz, noua clasă moşteneşte toate metodele şi variabilele clasei Thread care implementează
în mod standard, în Java, funcţionalitatea de lucru cu fire de execuţie. Singurul lucru pe care trebuie
să-l facă noua clasă este să reimplementeze metoda run care este apelată automat de către mediul
de execuţie la lansarea unui nou fir. În plus, noua clasă ar putea avea nevoie să implementeze un
constructor care să permită atribuirea unei denumiri firului de execuţie.
Dacă firul are un nume, acesta poate fi obţinut cu metoda getName care returnează un obiect de tip
String .
Iată un exemplu de definire a unui nou tip de fir de execuţie:
class FirNou extends Thread {
public FirNou( String nume ) {
// apelează constructorul din Thread
super( nume );
}
public void run() {
while( true ) , // fără sfârşit
System.out.println( getName() +
" Tastati ^C" );
}
}
}
Dacă vom crea un nou obiect de tip FirNou şi îl lansăm în execuţie acesta va afişa la infinit mesajul
"Tastaţi ^C". Întreruperea execuţiei se poate face într-adevăr prin tastarea caracterului ^C, caz în care
întreaga aplicaţie este terminată. Atâta timp însă cât noul obiect nu va fi întrerupt din exterior,
aplicaţia va continua să se execute pentru că mai există încă fire de execuţie active şi indiferent de
faptul că firul de execuţie principal s-a terminat sau nu.
Iată şi un exemplu de aplicaţie care foloseşte această clasă:
public TestFirNou {
public static void main( String[] ) {
new FirNou( "Primul" ).start();
}
}
Metoda start , predefinită în obiectul Thread lansează execuţia propriu-zisă a firului. Desigur există şi
căi de a opri execuţia la nesfârşit a firului creat fie prin apelul metodei stop, prezentată mai jos, fie
prin rescrierea funcţiei run în aşa fel încât execuţia sa să se termine după un interval finit de timp.
A doua cale de definiţie a unui fir de execuţie este implementarea interfeţei Runnable într-o anumită
clasă de obiecte. Această cale este cea care trebuie aleasă atunci când clasa pe care o creăm nu se
poate deriva din clasa Thread pentru că este important să fie derivată din altă clasă. Desigur,
moştenirea multiplă ar rezolva această problemă, dar Java nu are moştenire multiplă.
Această nouă cale se poate folosi în modul următor:
class Oclasa {
?
}
class FirNou extends Oclasa implements Runnable {
public void run() {
for( int i = 0; i < 100; i++ ) {
System.out.println( "pasul " + i );
}
}
?
}
public class TestFirNou {
public static void main( String argumente[] ) {
new Thread( new FirNou() ).start();
// Obiectele sunt create şi folosite imediat
// La terminarea instrucţiunii, ele sunt automat
// eliberate nefiind referite de nimic
}
}
După cum observaţi, clasa Thread are şi un constructor care primeşte ca argument o instanţă a unei
clase care implementează interfaţa Runnable . În acest caz, la lansarea în execuţie a noului fir, cu
metoda start , se apelează metoda run din acest obiect şi nu din instanţa a clasei Thread .
Atunci când dorim să creăm un aplet care să ruleze pe un fir de execuţie separat faţă de pagina de
navigator în care rulează pentru a putea executa operaţii în fereastra apletului şi în acelaşi timp să
putem folosi în continuare navigatorul, suntem obligaţi să alegem cea de-a doua cale de
implementare. Aceasta pentru că apletul nostru trebuie să fie derivat din clasa standard Applet .
Singura alternativă care ne rămâne este aceea de a implementa în aplet interfaţa Runnable .
4.2.2 Stările unui fir de execuţie
Un fir de execuţie se poate afla în Java în mai multe stări, în funcţie de ce se întâmplă cu el la un
moment dat.
Atunci când este creat, dar înainte de apelul metodei start, firul se găseşte într-o stare pe care o vom
numi Fir Nou Creat . În această stare, singurele metode care se pot apela pentru firul de execuţie
sunt metodele start şi stop . Metoda start lansează firul în execuţie prin apelul metodei run . Metoda
stop omoară firul de execuţie încă înainte de a fi lansat. Orice altă metodă apelată în această stare
provoacă terminarea firului de execuţie prin generarea unei excepţii de tip
IllegalThreadStateException .
Dacă apelăm metoda start pentru un Fir Nou Creat firul de execuţie va trece în starea Rulează . În
această stare, instrucţiunile din corpul metodei run se execută una după alta. Execuţia poate fi oprită
temporar prin apelul metodei sleep care primeşte ca argument un număr de milisecunde care
reprezintă intervalul de timp în care firul trebuie să fie oprit. După trecerea intervalului, firul de
execuţie va porni din nou.
În timpul în care se scurge intervalul specificat de sleep , obiectul nu poate fi repornit prin metode
obişnuite. Singura cale de a ieşi din această stare este aceea de a apela metoda interrupt . Această
metodă aruncă o excepţie de tip InterruptedException care nu este prinsă de sleep dar care trebuie
prinsă obligatoriu de metoda care a apelat metoda sleep . De aceea, modul standard în care se
apelează metoda sleep este următorul:
?
try {
sleep( 1000 ); // o secundă
} catch( InterruptedException ) {
?
}
?
Dacă dorim oprirea firului de execuţie pe timp nedefinit, putem apela metoda suspend . Aceasta
trece firul de execuţie într-o nouă stare, numită Nu Rulează . Aceeaşi stare este folosită şi pentru
oprirea temporară cu sleep . În cazul apelului suspend însă, execuţia nu va putea fi reluată decât
printr-un apel al metodei resume . După acest apel, firul va intra din nou în starea Rulează .
Pe timpul în care firul de execuţie se găseşte în starea Nu Rulează , acesta nu este planificat niciodată
la controlul unităţii centrale, aceasta fiind cedată celorlalte fire de execuţie din aplicaţie.
Firul de execuţie poate intra în starea Nu Rulează şi din alte motive. De exemplu se poate întâmpla ca
firul să aştepte pentru terminarea unei operaţii de intrare/ieşire de lungă durată caz în care firul va
intra din nou în starea Rulează doar după terminarea operaţiei.
O altă cale de a ajunge în starea Nu Rulează este aceea de a apela o metodă sau o secvenţă de
instrucţiuni sincronizată după un obiect. În acest caz, dacă obiectul este deja blocat, firul de execuţie
va fi oprit până în clipa în care obiectul cu pricina apelează metoda notify sau notifyAll .
În fine, atunci când metoda run şi-a terminat execuţia, obiectul intră în starea Mort . Această stare
este păstrată până în clipa în care obiectul este eliminat din memorie de mecanismul de colectare a
gunoaielor. O altă posibilitate de a intra în starea Mort este aceea de a apela metoda stop .
Atunci când se apelează metoda stop , aceasta aruncă cu o instrucţiune throw o eroare numită
ThreadDeath . Aceasta poate fi prinsă de către cod pentru a efectua curăţenia necesară. Codul
necesar este următorul:
?
try {
firDeExecutie.start();
?
} catch( ThreadDeath td ) {
? // curăţenie
throw td; // se aruncă obiectul mai departe
// pentru a servi la distrugerea
// firului de execuţie
}
Desigur, firul de execuţie poate fi terminat şi pe alte căi, caz în care metoda stop nu este apelată şi
eroarea ThreadDeath nu este aruncată. În aceste situaţii este preferabil să ne folosim de o clauză
finally ca în:
?
try {
firDeExecutie.start();
?
} finally {
..// curăţenie
}
În fine, dacă nu se mai poate face nimic pentru că firul de execuţie nu mai răspunde la comenzi,
puteţi apela la calea disperată a metodei destroy . Din păcate, metoda destroy termină firul de
execuţie fără a proceda la curăţirile necesare în memorie.
Atunci când un fir de execuţie este oprit cu comanda stop , mai este nevoie de un timp până când
sistemul efectuează toate operaţiile necesare opririi. Din această cauză, este preferabil să aşteptăm
în mod explicit terminarea firului prin apelul metodei join :
firDeExecutie.stop()
try {
firDeExecutie.join();
} catch( InterruptedException e ) {
?
}
Excepţia de întrerupere trebuie prinsă obligatoriu. Dacă nu apelăm metoda join pentru a aştepta
terminarea şi metoda stop este de exemplu apelată pe ultima linie a funcţiei main , există şansa ca
sistemul să creadă că firul auxiliar de execuţie este încă în viaţă şi aplicaţia Java să nu se mai termine
rămânând într-o stare de aşteptare. O puteţi desigur termina tastând ^C.
4.2.3 Prioritatea firelor de execuţie
Fiecare fir de execuţie are o prioritate cuprinsă între valorile MIN_PRIORITY şi MAX_PRIORITY. Aceste
două variabile finale sunt declarate în clasa Thread . În mod normal însă, un fir de execuţie are
prioritatea NORM_PRIORITY, de asemenea definită în clasa Thread .
Mediul de execuţie Java planifică firele de execuţie la controlul unităţii centrale în funcţie de
prioritatea lor. Dacă există mai multe fire cu prioritate maximă, acestea sunt planificate după un
algoritm round-robin. Firele de prioritate mai mică intră în calcul doar atunci când toate firele de
prioritate mare sunt în starea Nu Rulează .
Prioritatea unui fir de execuţie se poate interoga cu metoda getPriority care întoarce un număr
întreg care reprezintă prioritatea curentă a firului de execuţie. Pentru a seta prioritatea, se foloseşte
metoda setPriority care primeşte ca parametru un număr întreg care reprezintă prioritatea dorită.
Schimbarea priorităţii unui fir de execuţie este o treabă periculoasă dacă metoda cu prioritate mare
nu se termină foarte repede sau dacă nu are opriri dese. În caz contrar, celelalte metode nu vor mai
putea primi controlul unităţii centrale.
Există însă situaţii în care putem schimba această prioritate fără pericol, de exemplu când avem un fir
de execuţie care nu face altceva decât să citească caractere de la utilizator şi să le memoreze într-o
zonă temporară. În acest caz, firul de execuţie este în cea mai mare parte a timpului în starea Nu
Rulează din cauză că aşteaptă terminarea unei operaţii de intrare/ieşire. În clipa în care utilizatorul
tastează un caracter, firul va ieşi din starea de aşteptare şi va fi primul planificat la execuţie din cauza
priorităţii sale ridicate. În acest fel utilizatorul are senzaţia că aplicaţia răspunde foarte repede la
comenzile sale.
În alte situaţii, avem de executat o sarcină cu prioritate mică. În aceste cazuri, putem seta pentru firul
de execuţie care execută aceste sarcini o prioritate redusă.
Alternativ, putem defini firul respectiv de execuţie ca un demon. Dezavantajul în această situaţie este
faptul că aplicaţia va fi terminată atunci când există doar demoni în lucru şi există posibilitatea
pierderii de date. Pentru a declara un fir de execuţie ca demon, putem apela metoda setDaemon.
Această metodă primeşte ca parametru o valoare booleană care dacă este true firul este făcut
demon şi dacă nu este adus înapoi la starea normală. Putem testa faptul că un fir de execuţie este
demon sau nu cu metoda isDemon .
4.2.4 Grupuri de fire de execuţie
Uneori avem nevoie să acţionăm asupra mai multor fire de execuţie deodată, pentru a le suspenda,
reporni sau modifica prioritatea în bloc. Din acest motiv, este util să putem grupa firele de execuţie
pe grupuri. Această funcţionalitate este oferită în Java de către o clasă numită ThreadGroup .
La pornirea unei aplicaţii Java, se creează automat un prim grup de fire de execuţie, numit grupul
principal, main . Firul principal de execuţie face parte din acest grup. În continuare, ori de câte ori
creăm un nou fir de execuţie, acesta va face parte din acelaşi grup de fire de execuţie ca şi firul de
execuţie din interiorul căruia a fost creat, în afară de cazurile în care în constructorul firului
specificăm explicit altceva.
Într-un grup de fire de execuţie putem defini nu numai fire dar şi alte grupuri de execuţie. Se creează
astfel o arborescenţă a cărei rădăcină este grupul principal de fire de execuţie.
Pentru a specifica pentru un fir un nou grup de fire de execuţie, putem apela constructorii obişnuiţi
dar introducând un prim parametru suplimentar de tip ThreadGroup . De exemplu, putem folosi
următorul cod:
ThreadGroup tg = new ThreadGroup( "Noul grup" );
Thread t = new Thread( tg, "Firul de executie" );
Acest nou fir de execuţie va face parte dintr-un alt grup de fire decât firul principal. Putem afla grupul
de fire de execuţie din care face parte un anumit fir apelând metoda getThreadGroup , ca în
secvenţa:
Thread t = new Thread( "Firul de Executie" );
ThreadGroup tg = t.getThreadGroup();
Operaţiile definite pentru un grup de fire de execuţie sunt clasificabile în operaţii care acţionează la
nivelul grupului, cum ar fi aflarea numelui, setarea unei priorităţi maxime, etc., şi operaţii care
acţionează asupra fiecărui fir de execuţie din grup, cum ar fi stop , suspend sau resume . Unele dintre
aceste operaţii necesită aprobarea controloarelor de securitate acest lucru făcându-se printr-o
metodă numită checkAccess . De exemplu, nu puteţi seta prioritatea unui fir de execuţie decât dacă
aveţi drepturile de acces necesare.
4.2.5 Enumerarea firelor de execuţie
Pentru a enumera firele de execuţie active la un moment dat, putem folosi metoda enumerate
definită în clasa Thread precum şi în clasa ThreadGroup . Această metodă primeşte ca parametru o
referinţă către un tablou de referinţe la obiecte de tip Thread pe care îl umple cu referinţe către
fiecare fir activ în grupul specificat.
Pentru a afla câte fire active sunt în grupul respectiv la un moment dat, putem apela metoda
activeCount din clasa ThreadGroup . De exemplu:
public listeazaFire {
ThreadGroup grup = Thread.currentThread().getThreadGroup();
int numarFire = grup.activeCount();
Thread fire[] = new Thread[numarFire];
grup.enumerate( fire );
for( int i = 0; i < numar; i++ ) {
System.out.println( fire[i].toString() );
}
}
Metoda enumerate întoarce numărul de fire memorate în tablou, care este identic cu numărul de
fire active.
4.2.6 Sincronizare
În unele situaţii se poate întâmpla ca mai multe fire de execuţie să vrea să acceseze aceeaşi variabilă.
În astfel de situaţii, se pot produce încurcături dacă în timpul unuia dintre accese un alt fir de
execuţie modifică valoarea variabilei.
Limbajul Java oferă în mod nativ suport pentru protejarea acestor variabile. Suportul este construit
de fapt cu granulaţie mai mare decât o singură variabilă, protecţia făcându-se la nivelul obiectelor.
Putem defini metode, în cadrul claselor, care sunt sincronizate.
Pe o instanţă de clasă, la un moment dat, poate lucra o singură metodă sincronizată. Dacă un alt fir
de execuţie încearcă să apeleze aceeaşi metodă pe aceeaşi instanţă sau o altă metodă a clasei de
asemenea declarată sincronizată, acest al doilea apel va trebui să aştepte înainte de execuţie
eliberarea instanţei de către cealaltă metodă.
În afară de sincronizarea metodelor, se pot sincroniza şi doar blocuri de instrucţiuni. Aceste
sincronizări se fac tot în legătură cu o anumită instanţă a unei clase. Aceste blocuri de instrucţiuni
sincronizate se pot executa doar când instanţa este liberă. Se poate întâmpla ca cele două tipuri de
sincronizări să se amestece, în sensul că obiectul poate fi blocat de un bloc de instrucţiuni şi toate
metodele sincronizate să aştepte, sau invers.
Declararea unui bloc de instrucţiuni sincronizate se face prin:
synchronize ( Instanţă ) {
Instrucţiuni
}
iar declararea unei metode sincronizate se face prin folosirea modificatorului synchronize la
implementarea metodei.
4.2.7 Un exemplu
Exemplul următor implementează soluţia următoarei probleme: Într-o ţară foarte îndepărtată trăiau
trei înţelepţi filozofi. Aceşti trei înţelepţi îşi pierdeau o mare parte din energie certându-se între ei
pentru a afla care este cel mai înţelept. Pentru a tranşa problema o dată pentru totdeauna, cei trei
înţelepţi au pornit la drum către un al patrulea înţelept pe care cu toţii îl recunoşteau că ar fi mai bun
decât ei.
Când au ajuns la acesta, cei trei i-au cerut să le spună care dintre ei este cel mai înţelept. Acesta, a
scos cinci pălării, trei negre şi două albe, şi li le-a arătat explicându-le că îi va lega la ochi şi le va pune
în cap câte o pălărie, cele două rămase ascunzându-le. După aceea, le va dezlega ochii, şi fiecare
dintre ei va vedea culoarea pălăriei celorlalţi dar nu şi-o va putea vedea pe a sa. Cel care îşi va da
primul seama ce culoare are propria pălărie, acela va fi cel mai înţelept.
După explicaţie, înţeleptul i-a legat la ochi, le-a pus la fiecare câte o pălărie neagră şi le-a ascuns pe
celelalte două. Problema este aceea de a descoperi care a fost raţionamentul celui care a ghicit
primul că pălăria lui este neagră.
Programul următor rezolvă problema dată în felul următor: Fiecare înţelept priveşte pălăriile
celorlalţi doi. Dacă ambele sunt albe, problema este rezolvată, a lui nu poate fi decât neagră. Dacă
vede o pălărie albă şi una neagră, atunci el va trebui să aştepte puţin să vadă ce spune cel cu pălăria
neagră. Dacă acesta nu găseşte soluţia, înseamnă că el nu vede două pălării albe, altfel ar fi găsit
imediat răspunsul. După un scurt timp de aşteptare, înţeleptul poate să fie sigur că pălăria lui este
neagră.
În fine, dacă ambele pălării pe care le vede sunt negre, va trebui să aştepte un timp ceva mai lung
pentru a vedea dacă unul dintre concurenţii săi nu ghiceşte pălăria. Dacă după scurgerea timpului nici
unul nu spune nimic, înseamnă că nici unul nu vede o pălărie albă şi una neagră. Înseamnă că propria
pălărie este neagră.
Desigur, raţionamentul pleacă de la ideea că ne putem baza pe faptul că toţi înţelepţii gândesc şi pot
rezolva probleme uşoare. Cel care câştigă a gândit doar un pic mai repede. Putem simula viteza de
gândiri cu un interval aleator de aşteptare până la luarea deciziilor. În realitate, intervalul nu este
aleator ci dictat de viteza de gândire a fiecărui înţelept.
Cei trei înţelepţi sunt implementaţi identic sub formă de fire de execuţie. Nu câştigă la fiecare rulare
acelaşi din cauza caracterului aleator al implementării. Înţeleptul cel mare este firul de execuţie
principal care controlează activitatea celorlalte fire şi le serveşte cu date, culoarea pălăriilor, doar în
măsura în care aceste date trebuie să fie accesibile. Adică nu se poate cere propria culoare de
pălărie.
Culoarea iniţială a pălăriilor se poate rescrie din linia de comandă.
import java.awt.Color;
// clasa Filozof implementează comportamentul
// unui concurent
class Filozof extends Thread {
// părerea concurentului despre culoarea
// pălăriei sale. Null dacă încă nu şi-a
// format o părere.
Color parere = null;
Filozof( String nume ) {
super( nume );
}
public void run() {
// concurentii firului curent
Filozof concurenti[] = new Filozof[2];
// temporar
Thread fire[] = new Thread[10];
int numarFire = enumerate( fire );
for( int i = 0, j = 0; i < numarFire && j < 2; i++ ) {
if( fire[i] instanceof Filozof &&
fire[i] != this ) {
concurenti[j++] = (Filozof)fire[i];
}
}
while( true ) {
Color primaCuloare = Concurs.culoare( this,
concurenti[0] );
Color adouaCuloare =
Concurs.culoare( this, concurenti[1] );
if( primaCuloare == Color.white &&
adouaCuloare == Color.white ) {
synchronized( this ) {
parere = Color.black;
}
} else if( primaCuloare == Color.white ){
try{
sleep( 500 );
} catch( InterruptedException e ){
};
if( Concurs.culoare( this, concurenti[1]) != concurenti[1].aGhicit()) {
synchronized( this ) {
parere = Color.black;
};
}
} else if( adouaCuloare == Color.white ) {
try{
sleep( (int)( Math.random()*500));
} catch( InterruptedException e ) {
};
if( Concurs.culoare(this, concurenti[0] ) != concurenti[0].aGhicit()) {
synchronized( this ) {
parere = Color.black;
};
}
} else {
try {
sleep( (int)( Math.random()*500)+500 );
} catch( InterruptedException e ) {
};
if( Concurs.culoare(this, concurenti[0]) != concurenti[0].aGhicit() &&
Concurs.culoare( this,
concurenti[1] ) !=
concurenti[1].aGhicit() ) {
synchronized( this ) {
parere = Color.black;
};
}
}
}
}
public synchronized Color aGhicit() {
return parere;
}
}
public class Concurs {
private static Color palarii[] = {
Color.black, Color.black, Color.black
};
private static Filozof filozofi[] = new Filozof[3];
public static void main( String args[] ) {
for( int i = 0; i < args.length && i < 3; i++ ) {
if( args[i].equalsIgnoreCase( "alb" ) ) {
palarii[i] = Color.white;
} else if(args[i].equalsIgnoreCase("negru")) {
palarii[i] = Color.black;
}
}
for( int i = 0; i < 3; i++ ) {
filozofi[i] = new Filozof( "Filozoful " +
( i + 1 ) );
}
for( int i = 0; i < 3; i++ ) {
filozofi[i].start();
}
System.out.println( "Concurenti:" );
for( int i = 0; i < 3; i++ ) {
System.out.println( "\t" +
filozofi[i].getName() + " "
+ (( palarii[i] == Color.white ) ?
"alb":"negru" ) );
}
gata:
while( true ) {
for( int i = 0; i < 3; i++ ) {
if( filozofi[i].aGhicit()==palarii[i] ) {
System.out.println(
filozofi[i].getName() +
" a ghicit." );
break gata;
}
}
}
for( int i = 0; i < 3; i++ ) {
filozofi[i].stop();
try {
filozofi[i].join();
} catch( InterruptedException e ) {};
}
}
public static Color culoare( Filozof filozof,
Filozof concurent ) {
if( filozof != concurent ) {
for( int i = 0; i < 3; i++ ) {
if( filozofi[i] == concurent ) {
return palarii[i];
}
}
}
return null;
}
}
4.2.8 Un exemplu Runnable
Exemplul următor implementează problema celor 8 dame, şi anume: găseşte toate posibilităţile de a
aşeza pe o tablă de şah 8 regine în aşa fel încât acestea să nu se bată între ele. Reginele se bat pe
linie, coloană sau în diagonală.
Soluţia de faţă extinde problema la o tablă de NxN căsuţe şi la N regine. Parametrul N este citit din
tagul HTML asociat apletului.
import java.awt.*;
import java.applet.Applet;
public
class QueensRunner extends Applet implements Runnable {
int n;
int regine[];
int linie;
Image queenImage;
Thread myThread;
public void start() {
if( myThread == null ) {
myThread = new Thread( this, "Queens" );
myThread.start();
}
}
public void stop() {
myThread.stop();
myThread = null;
}
public void run() {
while( myThread != null ) {
nextSolution();
repaint();
try {
myThread.sleep( 1000 );
} catch ( InterruptedException e ){
}
}
}
boolean isGood() {
for( int i = 0; i < linie; i++ ) {
if( regine[linie] == regine[i] ||
Math.abs( regine[i] -
regine[linie] ) == Math.abs( i - linie ) ) {
return false;
}
}
return true;
}
void nextSolution() {
while( true ) {
if( linie < 0 ) {
linie = 0;
}
regine[linie]++;
if( regine[linie] > n ) {
regine[linie] = 0;
linie--;
} else {
if( isGood() ) {
linie++;
if( linie >= n ) {
break;
}
}
}
}
}
public void init() {
String param = getParameter( "Dimension" );
if( param == null ) {
n = 4;
} else {
try {
n = Integer.parseInt( param );
} catch( NumberFormatException e ) {
n = 4;
}
if( n < 4 ) {
n = 4;
}
}
regine = new int[n + 1];
for( int i = 0; i < n; i++ ) {
regine[i] = 0;
}
linie = 0;
queenImage = getImage(getCodeBase(), "queen.gif" );
}
public void paint( Graphics g ) {
Dimension d = size();
g.setColor( Color.red );
int xoff = d.width / n;
int yoff = d.height / n;
for( int i = 1; i < n; i++ ) {
g.drawLine( xoff * i, 0, xoff * i, d.height );
g.drawLine( 0, yoff * i, d.width, yoff * i );
}
for( int i = 0; i < n; i++ ) {
for( int j = 0; j < n; j++ ) {
if( regine[i] - 1 == j ) {
g.drawImage(queenImage,
i*xoff + 1, j*yoff + 1, this);
}
}
}
}
public String getAppletInfo() {
return "Queens";
}
}
top related