1. Limbajul
Java in aplicatii industriale
Evolutia
tehnologica din ultima perioada, in mod evident datorata mediului atit economic
cit si universitar puternic concurential din lumea capitalista, a dus la o
fantastica explozie a industriei electronice, a tehnicii de calcul si
informatice. Concurenta acerba impreuna cu cantitatea si nu in ultimul rind
calitatea persoanelor implicate in activitatile de cercetare si productie atit
in domeniul hardwarelui cit si a softwarelui au determinat salturi tehnologice
regulate si la intervale scurte de timp, astfel cei care au fost de fata la
nasterea calculatoarelor si la primi pasi in domeniul informatic vor recunoaste
cu greu urmasi uriasilor, mari consumatoare de energie si lentelor
calculatoare, si cu atit mai greu metodele de programare avansate pe acea vrem
in stilurile si filozofiile programarii din ziua de azi.
Cu
toate acestea, desi domeniul automatizarilor nu a fost neglijat, performantele
dispozitivelor implicate in procesul de automatizare, dublindu-se in fiecare
an, in domeniul programarii acestor dispozitive au intervenit foarte putine
modificari, folosindu-se in continuare limbaje in cod masina sau asamblare,
foarte modeste din punct de vedere a resurselor necesare, si foarte performante
ca timpi de executie, dar foarte frustrate la utilizare si mari consumatoare de
resurse umane. Unele imbunatatiri, au aparut pe parcursul timpului, cum ar fi
compilatoarele de C, care generau codul masina pentru diverse platforme, dar
la nivel de microdispoztive per ansamblu lucrurile se desfasoara in aceiasi
maniera de o perioada lunga de vreme. La un nivel superior, si ma refer la cel
al conducerii proceselor, lucrurile stau cu o idee mai bine, o serie de
limbaje de mare calitate si continind tehnologiile de ultima ora au fost
dezvoltate si utilizate, si a-si aminti aici ADA, un limbaj care desi conceput
in anii 70, inca este viabil si la moda. Totusi, desi avind tangente cu
limbajele utilizate in mod curent la dezvoltarea aplicatiilor comerciale, ele
erau subseturi specializate pe diferite taskuri sau tipuri de taskuri, si
diferite platforme. Ei, toate acestea incepind cu anul 1996 tind sa se schimbe,
multumita companiei Sun, care desi nu era activa pe piata echipamentelor de
automatizare a sprijinit cercetarile unui inginer in elaborarea unui limbaj de
programare destinat echipamentelor de uz casnic. Recunoscind potentialul unui
asemenea limbaj, echipa manageriala a companiei a hotarit ca e momentul sa se
canalizeze mai multe resurse in aceasta directie, si impreuna cu o strategie de
marketing briliant sa i-l impuna in timp scurt pe piata. Piata sesizind
potentialul imens al, limbajului de programare propus de Sun, limbaj care intre
timp a fost extins in directii deosebite (ma refer la orientarea puternica
citre Internet si retele de calculatoare in special, precum si la faptul ca
este printre foarte putinele limbaje de programare care au fost gindite in
vederea portabilitati totale a codului generat pe diferite platforme) l-a
adoptat cu frenezie, ajungind ca firme de renume precum IBM Novel sau
Havlet-Packard sa puna o mare baza pe el in planurile lor de viitor.
Obiectul
proiectului de fata, este prezentarea limbajului Java si in special a acelor
facilitati ale limbajului, care au o importanta mai mare pentru domeniul
Informatici Industriale si a Automatici in general. Pentru a demonstra
adecvarea limbajului, acestui mediu de problematici, proiectul i-si propune
demonstrarea avantajului tehnicilor moderne de programare, programarea
orientata pe obiecte, avantaje pe care le aduce din punct de vedere al
structurari aplicatiei, al reutilizari codului, si al flexibilitati si
extensibilitatii codului astfel scris. Totodata, proiectul i-si mai propune,
demonstrarea facilitatilor aduse de limbajul Java, cum ar fi tratarea
exceptiilor, faptul ca este interpretat, si multitasking, facilitatile sale
legate de Internet si comunicatia de date in general. Toate acesta, prin
implementarea unor instrumente virtuale, capabile de comunicatie prin medii
eterogene, bazate pe retele de calculatoare si deci capabile de a distribui
semnalul. Bazat pe aceste instrumente virtuale se prezinta un simulator de
proces, simulatie care tocmai din cauza ca foloseste instrumentele virtuale
este foarte aproape de o implementare realista a procesului. Totodata se
prezinta o metoda, deosebit de usoara, bazata pe metode vizuale, a de
implementa o interfata, a procesului, si de a face procesul public, utilizind o
retea locala de calculatore (Internet) sau internetul. Gradul de noutate al
temei proiectului precum si al ideilor expuse in el este destul de mare. Metode
de monitorizare a unui proces utilizind Internetul, s-au realizat pina in
momentul de fata, numai de catre NASA, care este un sistem proprietar, si de
doua companii americane, ambele proiecte fiind in stadii incipiente, si toate
trei avind ca limbaj principal de implementare limbajul Java.
1.1. Prezentarea
generala a limbajului Java
1.1.1. Istoria
limbajului Java
Java
a fost denumit initial Oak, si dezvoltat pentru a fi folosit in aplicati care
tinteau de piata bunurilor electronice de larg consum de catre James Gosling.
Dupa o perioada de citiva ani de experimentari cu noul limbaj si dupa ce si-au
adus contributia o serie de persoane, din care-i amintim pe: Ed Frank, Patrick
Naughton, Jonathan Payne si pe Chris Warth, limbjul a fost reorientat catre
Internet, redenumit Java si revizuit substantial. Forma final a limbajului a
fost definita de catre James Gosling, Bill Joy, Guy Steele, Richard Tuck, Frank
Yellin si Artur van Hoff ajutat de catre Graham Hamilton, Tim Lindholm si alti.
Aceasta a fost povestea pe scurt, in realitate totul a pornit in anul 1994 cind
James Gosling facea parte dintr-o echipa de cercetare izolata in cadrul
companiei Sun, echipa care avea ca scop determinarea unor metode de a introduce
informatica si calculatoarele in dispozitivele de larga folosinta. Echipa dorea
sa realizeze dispozitive cu un grad mai ridicat de interactiune cu utilizatorul
uman dar care sa fie capabile sa interactioneze si intre ele. Pentru a-si crea
o idee realista despre cum ar trebui sa functioneze si sa comunice aceste
dispozitive Green a construit un dispozitiv prototip numit Star7. Acest
dispozitiv era o telecomanda operata prin atingerea unor obiecte animate de pe
ecranul acesteia. Un utilizator a lui Star7 putea naviga prin atingeri
printr-un univers de camere si obiecte, de altfel eroul principal al
universului era un omulet (Duke) imortalizat mai tirziu ca mascota limbajului.
Facilitatea
cea mai remarcabila a lui Star7 a fost capacitatea lui de a comunica cu alte
dispozitive Star7. Un obiect de pe displayul unui Star7 putea fi pasat de la un
dispozitiv la altul. Prototipul era un sistem de operare distribuit in care
fiecare dispozitiv facea parte dintr-un tot unitar, exact genul de sistem
necesar pentru ca un frigider sa-I comunice, de exemplu, aspiratorului ca
acesta sa-I comunice operatorului uman ca daca nu este decongelat si curatat nu
i-si va mai putea exersa functiile de congelare.
Planul
initial a fost ca sistemul de operare pentru Star7 sa fie dezvoltat in C++. Dar
dupa cum a afirmat Goshling la conferinta JavaOne din May 1996:
“The
tools kept breaking. It was at a fairy early breaking point when I was so
disgusted that I went to my office and started typing.”
Asadar,
astfel s-a inceput scrierea unui limbaj nou de programare care era mai adaptat
acestui tel decit C++, limbajul primind numele de Oak, in cinstea copacului din
fata ferestrei sale.
Din
start acest limbaj de programare a fost conceput in asa fel incit sa permita
crearea de programele mici, fara erori si cu facilitati de comunicare prin
retele de dispozitive si calculatoare. Asemanator C++, Oak era orientat pe
obiecte, o metoda deosebit de puternica de a crea programe de calculator, si
care ofera o multime de avantaje fata de metodele conventionale, dar si
impedimentul ca este mult mai dificil de stapinit, dar spre deosebire de acesta
a fost conceput tocmai in a facilita o invatare mai usoara fata de restul
limbajelor orientate pe obiecte.
Programele
Oak, trebuiau de asemenea sa ruleze de o maniera independenta de platforma,
deoarece producatori dispozitivelor de larg consum care urmau sa-l foloseasca
trebuiau sa aiba abilitatea de a modifica un procesor sau microcontroler mai
scump cu unul mai ieftin de cite ori piata le cere sa reduca costul produselor.
Iar in contrast cu un posesor de calculator personal, un utilizator al unui
produs casnic va fi mai putin dispus sa cumpere un coprocesor pentru a mari
puterea de calcul, sa zicem a unei risnite. Pe de alta parte un astfel de
consumator va fi mai putin tolerant cu erorile de programare, care ar putea
sa-I faca prajitorul de piine inutilizabil. In aceiasi perioada Marc Andersen,
un student care lucra la National Center for Supercomputing Applications,
crea primul browser pentru World Wide Web, Mosaic 1.0. Pe la mijlocul anului
1994, au gasit World Wide Webul ca fiind foarte promitator din punctul de
vedere al limbajului pe care i-l dezvoltau, desi era un servicii mai cunoscut
doar in restrinsa comunitate a fizicienilor, oricum browserul grafic a lui
Andersen a stirnit un fenomen international, iar Web-ul a devenit rapid un
mediu mas-media . Tehnologia dezvoltata de Oak, era perfect pregatita pentru
acest mediu, in special datorita capacitatilor sale de a rula pe mai multe
platforme si facilitatilor de comunicare prin retele, dar cel mai important a
introdus ceva ce nu mai fusese posibil pina atuncea, posibilitatea de a rula
aplicatii de pe serverele Web pe calculatoarele utilizatorilor in siguranta.
Patrick
Naughton si Jonathan Payne, au terminat WebRunner, un browser de Web care a
adus din nou la lumina vedeta proiectului Star7, Duke. Sun realizind ca are
ceva promitator in minã se pregãteste de lansarea produsului,
doar cã numele de Oak era deja patentat. Astfel dupã o
sedintã de brainstorming din Januarie 1995 de a inlocui numele de Oak,
numele de Java a fost votat in locul lui Oak si HotJava in locul lui
WebRunner, iar in Noiembrie 1995 prima versiune de Java (beta) a fost facuta
disponibila pentru oricine pe site-urile, firmei, fiind cautat de zeci de mii
de persoane si sute de firme.
E
adevarat ca cuptoarele cu microunde nu au devenit mai destepte, dar partea de
control nu a fost neglijata cu toate ca versiunea principala nu este conceputa
pentru uz industrial sau in dispozitive cu capacitati restrinse, Sun a lansat
doua linii paralele de Java, PersonalJava, pentru dispozitive limitate ca si
capacitati dar cu posibilitati de afisare, si Embedded Java pentru dispozitive
limitate ca si capacitati sau resurse si fara nici un mijloc de afisare.
1.1.2. Descrierea
limbajului Java
Limbajul
Java a fost conceput pentru a rezolva o parte din problemele informaticii
aplicate, astfel caracteristicile lui sint :
- simplitate
- orientare
pe obiecte
- puternic
orientat spre retele de calculatoare
- interpretat
- robust
- sigur
- neutru
din punct de vedere al arhitecturii
- portabil
- de
inalta performanta
- ofera
suport pentru multithreading
- este
un limbaj legat dinamic
Asa
cum am mai amintit, unul din dezideratele echipei de ingineri care au proiectat
si implementat acest limbaj a fost simplitatea. Analizind cum lucreaza
programatorii in general au ajuns la concluzia ca cel mai utilizat limbaj de
programare in ziua de azi este limbajul C, iar pentru cei care lucreaza in
tehnologie orientata pe obiecte utilizeaza limbajul C++. Desi au pornit de la
premiza ca limbajul C++ nu asigura suport suficient pentru nevoile lor, au
incercat sa aduca limbajul Java cit mai aproape de sintaxa limbajului C++,
tocmai pentru a fi mai usor de invatat, si pentru a reduce timpul de adaptare
al utilizatorului la noul limbaj. Mai mult, au fost eliminate acele facilitati
care in opinia expertilor produceau mai multa confuzie, decit sa aduca
beneficii, astfel printre elementele eliminate din Java si care subzista in
limbajul C++, se pot enumara:
- supradefinirea
operatorilor (“ operator
overloading ”),
desi Java ofera suport puternic pentru supradefinirea metodelor (“ method
overloading” );
- mosteniri
multiple (“ multiple
inheritance” )
- conversii
automate a tipurilor de date (“ extensive
automatic coercions ”)
- pointerii
la date
S-au
adaugat facilitãti de ultima ora, dintre care putem aminti, managmentul
automat al memoriei, (“ automatic
garbage collection”) ,
simpificind imens munca programatorului, in schimbul complexitati mai ridicate
a sistemului. O sursa comuna generatoare de complexitate si probleme, este
gestionarea memoriei, alocarea si eliberarea ei, la momente bine specificate de
timp. Prin facilitatea oferita de un mechanism automat de gestionare a
memoriei a carui functie primara s-ar putea descrie prin dealocarea resurselor
nefolosite in mod automat. Prin aceasta s-a reusit nu numai inlesnirea muncii
programatorului, dar si reducerea imensa a timpului necesar finalizarii unui
proiect, deoarece eliminind problema gestionarii memoriei se elimina automat
cea mai mare sursa generatore de scame, bug-ri.
Un
alt aspect al faptului de a fi simplu este de a fi mic, conceput special penru
dispozitive cu capacitati reduse (microcontrolere, etc), deci capabil de a rula
in astfel de dispozitive, interpetorul limbajului cit si librariile acestia au
dimensiuni foarte reduse.
Orientat
pe obiecte inseamna destul de mult pentru lumea programatorului, dar din pacate
aceasta functionalitate nu a fost oferita programatorului automatist, nu pina
acum. S-a facut destul de multa vilva in jurul acestei notuni in ultima
perioada, dar adevarul este ca folosind modelul programarii orientat pe obiecte
se pot elabora programe mult mai clare, mai curate si mai apropiate de modul de
gindire uman, ca sa nu mai amintim de reutilizare codului. Descris foarte pe
scurt, tehnica pune accentul pe date (obiecte), si pe metodele de a le acesa. O
analogie larg folosita in literatura de specialitate este cea a timplarului. In
tehnologia orientata pe obiecte un tinplar ar fi concentrat in special pe
scaunul pe care-l construieste, si abia pe urma cu uneltele cu care sa-l
contruiasca. In metodologiile neorientate pe obiecte, motivul principal de
ingrijorare al tinplarului ar fi uneltele cu care sa construiasca scaunul si
abia pe urma cu scaunul. De asemenea modelul orientat pe obiecte, ofera
mecanismul pentru a defini cum modulele se interconecteaza intre ele. In esenta
facilitatle din puncul de vedere al orientarii pe obiecte, sunt cele oferite de
limbajul C++, extinse cu facilitatile oferite de Objective C pentru a oferi o
dinamica mult mai mare metodelor sale.
Suportul
oferit pentru retele de calculatoare este deosebit, este adevarat ca nu este
singurul limbaj de programare care ofera acest tip de suport, dar modul in care
este integrat in limbaj, si usurinta cu care se implementeaza mecanisme
deosebit de complexe il face unic in acest domeniu. Librariile si rutinele
implementate asigura suport pentru protocoale TCP/IP precum HTTP si FTP. Se
poate afirma ca este mult mai simplu si usor de a realiza o conexiune in retea
utlilizind limbajul Java decit limbajele C sau C++, programatorul reusind sa
aceseze obiecte din retea aproape la fel de simplu si usor de parca s-ar lucra
cu sistemul local de fisiere.
Robustetea
limbajului este asigurata de o serie de mecanisme de inalta performanta, cum ar
fi verificarea timpurie a programelor pentru posibile probleme, verificare
dinamica tirzie (runtime), si fortarea programatorului sa elimine situatiile
care sunt presupuse ca ar putea genera probleme la rulare. Unul dintre
avantajele limbajelor puternic tipizate (cum ar fi C++) este ca asigura un
puternic suport pentru verificarea programelor in timpul compilari in idea de a
elemina un numar cit Mai mare de errori, din pacate limbajul C++ mosteneste o
serie de probleme la verificarea in timpul compilarii tocmai din C, si acesta
doar pentru a pastra compatibilitatea in jos. Deoarece Java nu are ce
cobatibilitate in jos ce sa mentina, nu este nevoit sa suporte consecintele
unor implementari sau specificatii vechi proaste sau incomplet facute. Un
exemplu, declaratiile, pe cind in C++, se permit declaratii implicite ale
variabilelor, functiilor sau procedurilor (tocmai mostenite din C), Java este
foarte sever in declararea exacta a lor. Chiar si linkeditorul mai repeta
aceleasi verificari pentru a fi sigur ca nu apar probleme de incompatibilitati
intre tipuri de date folosite.
Cu
toate acestea cea mai mare diferenta intre limbajul Java si C, este ca din Java
sau eliminat complet pointerii. Motivul pentru acesta actiune radicala este ca,
pointerii reprezinta un mecanism deosebit de priculos din punctul de vedere al
posibilelor probleme pe care le pot genera (errori foarte dificil de gasit si
corectat), si o potentiala problema de securitate, data de nerestrictionarea
accesului lor (este deosebit de usor sa scriem/citim dintr-o zona de date
interzisa, respectiv sa corupem date, in mod intentionat sau nu). Mecanismul
folosit in schimb este cel al handlerelor si al tablourilor reale de date,
nefiind permisa modificarea unui tip intreg de date intr-un handler, si astfel
accesarea necontrolata a unei zone de memorie. Prin aceste modificari
(inbunatatiri) problemele de asigurarea calitatii (QA) din pacate nu dispar,
munca devenind insa incomparabil de simpla.
Firmele
profesioniste de software folosesc, de obicei, limbaje foarte dinamice cum ar
fi: TCL; Lisp; Smaltalk, in fazele de elaborare a prototipului de aplicatie
tocmai din aceste cauza ca aceste limbaje sunt foarte robuste, nefiind nevoiti
sa-si faca griji din pricina eliberarii memoriei sau coruperea datelor.
Programatorii Java putind fi relativ fara griji, din pricina lucrului cu
memoria, tocmai din cauza ca nu trebuie sa lucreze direct cu ea. Din cauza ca
nu sunt pointeri nu trebuie sa-si faca griji ca ar putea depasi, limitele
vectorului cu care lucreaza si sa suprascrie accidental alte date, sau sa
capate acces neutorizat la zone de memorie care nu-I apartin, fenomene care,
din pacate, se pot intinpla in C sau C++.
Un
motiv pentru care limbajele dinamice sunt bune pentru elaborarea prototipurilor
este ca ele nu te oblica sa iei decizii explicite de implementare prea devreme.
Java se apropie de acesta problema dintr-o alta directie, te obliga sa
implementezi stratregiile explicit deoarace la baza are o legare dinamica
fortata de compilator, dar odata cu aceste implementari sunt insotite o serie
de ajutoare sau Mai degraba asistenta, deoarece de exemplu la invocarea unei
metode, daca se obtine ceva gresit programatorul este instiintat in timpul
compilarii si nu in timpul executiei, cind invocarea respectiva se intinpla
efectiv.
Securitatea
a fost deasemenea un punct vizat pe parcusul elaborarii limbajului, si aceasta
cu atit mai mult cu cit a fost vizat sa atinga mediile orientate pe retele si
programare disribuita, permitindu-se construirea de medii eliberate de virusi
sau programe cu intentii obscure, impelemntand de asemenea si librarii de
functii avansate de criptografie, majoritateta bazate pe tehnologii de ultima
ora (chei publice). Exista o puternica interdependenta intre robustete si
securitate, eliminindu-se pointerii se elimina practic orice metoda de a forta
accesul la structurile de date al altor aplicatii sau la accesarea membrilor
protejati sau privati la care in mod normal nu au acces, din cadrul
programului, aceasta inchizind usa in fata majoritati cailor de acces a
virusilor.
Neutralitatea
fata de aritecturile calculatoarelor pe care se ruleaza, a este cerinta
principala a unui limbaj puternic orientat catre alicatii distribuite, deoarece
o aplicatie distribuita presupune existenta unui mediu, de obicei neomogen de
sisteme, interconectate intr-un anume fel, in mod uzual prin retele de
calculatoare. Pentru a permite unui program sa se execute pe o varietate de
procesoare si pe o varietate de sisteme de operare, compilatorul generaza un
fisier intr-un frmat ce contine obiecte neutre din punct de vedere arhitectural
(“ architecture-neutral
object file-format”) ,
fisier ce contine codul compilat, in acest fel capabil sã se execute pe
o diversitate de arhitecturi. Acesta este folositor nu numai pentru aplicatii
distribuite, dar si pentru programele normale (generale), deoarece nu mai este
nevoie de a scrie versiuni diferite sau customizate pentru anumite medii, fiind
suficienta elaborarea unei singure versiuni care nici macar nu trebuie
recompilata pentru a rula pe diferite platforme, rulind instant. Acest fapt
este un progres semnificativ avind in vedere tendintele actuale din domeniul
industriei producatoare de sisteme de calcul, si ma refer la piata
calculatoarelor care incepe sa se diversifice (prin Windows/NT) pe din ce in ce
Mai multe arhitecturi de procesoare, si la tranzitia firmei Apple dinspre
arhitecturile bazate pe microprocesoarele 680x0 inspre PowerPC-uri. Java
atingind acest deziderat atit prin generarea unui cod obiect indebendent de
platforma, cod binar, care a fost optimizat tocmai in vederea obtinerea
independentei de platforma cit si in vederea executiei usoare sau a
transformari, din zbor, in cod masina specific platformei respective.
A
fi independent de platforma este doar primul pas spre a fi complet portabil, si
contrar de exemplu limbajelor C sau C++, nu exista specificatii, librarii,
tipuri de date dependente de platforma implementate in Java. Tipurile primitive
de date, la fel ca si operatiile asupra lor sunt specificate si implementate in
mod unitar, indiferent de tipul procesoarelor pe care vor rula (8, 16, 32 sau
64 de biti). De exemplu un intreg int va
insemna intotdeauna un intreg cu semn reprezentat in complement de doi pe 32 de
biti, iar tipul float
va insemna intotdeauna un numar in virgula flotanta conform specificatiilor
standardului IEEE754. Librariile care sunt parti ale sistemlui definesc
interfete portabile, de exemplu exista o class fereastra abstracta si implicit
implementarile ei pentru sistemele de operare: Unix, Windows 95, Windows NT si
Machintosh.
Insusi
Java ca atare este destul de usor portabil, compilatorul fiind scris in Java,
si deci implicit portabil, iar mediul interpretor este scris in ANSI C, cu un
comportament curat si destul de portabil fiind bazate pe un subset al POSIX-ului.
Codul
binar Java este translatat din zbor in cod masina nativ, nefiind deci nevoie de
vre-o metoda de stocare, precum si datorita faptului ca procesul de linkeditare
este unul incremental si nu foarte costisitor ca resurse, procesul de
dezvoltare este mult Mai rapid si ofera mult Mai multe grade de libertate de
experimentare, Deasemenea tot datorita fluxului de cod binar proprietar
sistemului Java, Mai multa informatie este stocata in formatul binar, facind
astfel operatia de debuging mult nmai facila, ne Mai vorbind de faptul ca un
dezansamblor, este in masura sa restabileasca codul in procent de aproape 100%
la forma sa initiala, lucru care la alte limbaje este practic imposibil. La
conceperea formatului byte-codeului s-a tinut cont de faptul ca acesta uremaza
a fi intrepretat si transformat direct in cod masina specific platformei pe
care se lucreza si deci este putenic optimizat, realizind in timpul conversiei
si optimizarile necesare. Din studiile de performata realizate de catre firma
Sun, a rezultat un numar de 300.000 de apeluri de metode pe secunda, rezultat
obtinut pe un calculator Sun Microsystems SPARCStation 10, performantele
conversiei codului binar in cod masina, fiind la aproape la fel de bune ca si a
rulari unui program C, nativ.
Bineinteles,
nu se putea concepe un limbaj nou in ziua de azi fara a asigura suport pentru
multithreading. La fel ca in lumea care ne inconjoara, unde Mai multe lucruri
se intinpla simultan, acelasi lucru se poate face, sau simula si pe
calculatoarele noastre, numai ca a scrie un program multithreding nu este chiar
atit de simplu nici macar in limbaje ca C sau C++. Java are insa un puternic
set de mecanisme care sa asigure sincronizarea primitivelor, mecanisme bazate
pe raspinditele metode de monitorizare si conditionare introduse de
C.A.R.Hoare. Integrind toate aceste mecanisme in nucleul limbajului ele devin
mult Mai simplu, robust si Mai usor de folosit. Un alt avantaj al
multithreadingului sunt un raspuns mult mai interactiv si comortament in timp
real. Bineinteles, acesta este limitat de platforma care aigura tot suportul
pentru aplicatie, mediul, ca atare Java avind un bun raspuns in timp real, dar
rulind deasupra unor sisteme de operare, precum: Unix, Windows95, Windows NT,
Machintos, raspunsul sistemului si modul de tratare al taskurilor este cel care
determina raspunsul in timp real al aplicatiei scrise in Java.
Dinamicitatea
limbajului o depaseste de asemenea pe cea limbajelor conventionale, bunaoara
daca o companie oarecare A, produce o librarie, sau o componenta activa, si o
companie B o cumpara si foloseste, si compania A produce o noua veriune a
aceleiasi componente atunci pentru a o integra compania B trebuie Mai mult ca
sigur sa-si recompileze intreaga aplicatie si sa o redistribuie din nou
clientilor sai.
Facind
conexiunile intremodule de abia la rulare, Java elimina cu succes si total
acest tip de probleme, indiferent cite modificari ar fi intervenit in libraria
producatorului A (cu pastrarea compatibilitatii, totusi), Java se va executa
fara recompilari si probleme.
Acest
mecanism este sprijinit de modul cum sunt concepute aplicatiile Java, o
interfata specifica un set de metode si de obiecte pe care le poate executa,
dar nuspecifica si cum trebuie executate O clasa implementeaza o interfata
implementind toate obiectele si metodele interfetei. De asemenea mostenirea
paseaza atit metodele cit si implementarea lor de la superclasa la subclasa. O
clasa este capabila sa mosteneasca numai o singura clasa, dar poate implementa
cite interfete doreste. Deci se poate observa ca interfetele promoveaza
reutilizarea codului, si conecteaza elementele intre ele specificind Mai
degraba ce se conecteaza si nu cum se face. Un alt avantaj este identificarea
in timpul rulari al tipului de data, si acesta printr-un mecanism foarte
ingenios. Fiecare instanta de obiect are o metoda numita getClass
prin care se poate determina din ce clasa sa instantiat metoda respectiva, si
in acest mod se poate determina foarte elegant tipul obiectului respectiv.
Acesta facilitate este, de exemplu inexistenta in C sou C++. Tot prin aceasta
metda se executa si verificarile de rigoare din timpul rularii, si aceasta
deoarece in Java programele sunt verificate atit in timpul compilari cit si in
timpul rulari. Astfel, se poate avea deplina incredere in conversiile de date
executate in Java, deoarece sunt verificate de doua ori, pe cind bunaoara in C
sau C++, limbajul merge pe incredere, se bazeaza pe faptul ca programatorul a
executat o conversie de date (cast) corecta, cea ce nu intotdeauna este
adavarat, din pacate. Ba chiar ca o facilitate in plus, se poate cauta (in
timpul rulari), o clasa specificindui numai nuele intr-un sir de charactere, si
chiar Mia mult odata gasita, clasa se poate incarca si instantia, oferind un
dinamism complet aplicatiilor (acesta tehnica este dealtfel folosita si in
cadrul acestui proiect, si va fi exmplificata pe larg in capitolele urmatoare.
Ca
o prima concluzie putem desprinde, ca limbajul Java se dovedeste a fi o
puternica unealta de dezvoltare, dotata cu facilitati de ultima ora, care se
afla la dispozitia programatorului. Java, face programarea mult Mia usoara,
deoarece este orientat pe obiecte, si are management automat al memoriei,
aditional deoarece codul compilat este neutru din punct de vedere
arhitectural, limbajul este ideal pentru diverse aplicatii orientate pe
Internet.
1.1.2.1. Principii
de implementare
Java
prezinta un nou punct de vedere, in evoutia limbajelor de programare, crearea
unui limbaj simplu, mic si familiar care este totusi suficient de comprehesiv
pentru a se adresa unei varietati largi de problematici, in cadrul procesului
de ezvoltare de programe. Pe cind Java arata la suprafata ca si C sau C++, el
si-a cistigat simplitatea prin eliminarea unor facilitati care produceau
confuzie, sau impuneau o compatibilitate in jos care nu i-si Mia avea sensul.
In acest subcapitol se vor discuta doua dintre faciltatile primoridiale
reentate de Java, si anume, simplu (prin eliminarea anumitor facilitati) si
familiar (deoarece arata ca si limbajul C si C++).
Simplitatea
a fost principalul obiectiv impus de catre echipa de programatori care au
implementat si specificat acest limbaj. Simplitatea si eliminarea sistematica a
multor facilitati de o valoare “dubioasa” din familia de limbajele
C si C++, sau din predecesori lor, au mentinut Java la o dimensiune mica, si au
redus efortul programatorrilor de a produce aplicatii reliabile. Pina al
finaliza echipa care a conceput Java au analizat toate facilitatile pe care le
au lmbajele de programare “moderne ” din familia limbajelor C si
C++, pentru a determina facilitatile care trebuie introduse sau eliminate in
contextul unei limbaj de programare orientat pe obiecte modern.
Un
alt deziderat major, a fost familiaritatea, pentru majoritatea programatorilor
atit din sfera calculatoarelor personale cit si din cea a statiilor puternice
de lucru, unde marea majoritate a programatorilor atit din sfera
programatorilor sistem cit si a celor de aplicatii cunosc limbajul C sau C++.
Astfel programatori, care sunt familiarizati cu unul sau Mia multe din
limbajele urmatoare: C, Objective C, C++, Eiffel, Ada, si alte limbaje
aferente, ar trebui sa observe ca curba de invatare a limbajului Java este
foarte scurta, de ordinul saptaminilor.
Pentru
a demonstra simplitatea limbajului, vom continua traditia de a prezenta vesncul
exemplu al cartilor de programare, cu care se incepe, teoretic orice curs de
programare, si anume “Hello
world” :
class
HelloWorld {
static
public void main (String args[]) {
System.out.println(“Hello
World !”);
}
}
Acest
exemplu nu face altceva decit sa afiseze, un simplu text “Hello
World!” la consola, si o face aceasta declarind o clasa HelloWorld.
Din cite se poate vedea clasa noastra are numai o singura metoda, numita main. Aceasta
metoda are din cite se poate observa numai o singura linie, linie care nu face
altceva decit sa tipareasca, textul din ghilimele la consola. Aceasta se face
prin metoda println,
din obiectul out
care face parte din clasa System,
clasa care opereaza diverse operatii de intrare-iesire pe fisiere.
1.1.2.2. Tipuri
de date
Absoulut
totul exceptind, tipurile de date discutate aici sunt obiecte. Fiecare din
aceste tipuri de date primordiale pot fi “infasurate”, utilizind
anumite metode din librariile standard ale limbajului, in obiecte. Deci la
nevoie si aceste tipuri primitive de date pot fi transformate in obiecte,
beneficiind astfel de toate avantajele obiectelor in limbajul Java.
1.1.2.2.1 Tipuri
numerice
Tipurile
de date intreg sunt: 8-biti
byte
,
16-biti
short
, 32-biti
int,
si 64-biti
long
Tipul pe 8 biti
byte
a inlocuit vechiul tip de data din C si C++
char,
si asta deoarece in Java s-a implementat un tip specific de character,
char,
dupa
cum se va veda Mia jos. Nu Mia exista tipuri de date fara semn.
Tipurile
reale de intregi sunt: 32-biti
float
, si 64-biti
double
. Tipurile reale de date din Java precum si operatiile aritmetice aupra lor au
fost implementate conform standardului IEEE 754.
1.1.2.2.2 Tipul
Character
Tipul
standard de character din Java, este puternic delimitat de cel din
implementarea pe care o furnizeaza C. Tipul de data character,
char
este implementat pe 16 biti in contradictie cu implementarile marii majoritati
a limbajelor de programare care asigura o baza de numai 8 biti. De ce aceasta
diferenta. Acesta este reprezentarea impusa de modul de codificare UNICODE al
caracterelor, care a urmat modului ASCII folosit in majoritatea programelor,
aplicatiilor si sistemelor de operare. Codurile UNICODE sunt valori vara semn,
cuprinse in domeniul 0 - 65.535 , deci stocate pe 16 biti. Adoptind standardul
UNICODE limbajul Java devine un limbaj ideal pentru a asigura suport deoasebit
de larg pentru aplicati internationalizate.
1.1.2.2.3 Tipul
Logic
Java
a mai adaugat si tipul de data logic, ca tip de data fundamental, astfel o
variabila booleana poate avea valorile
true
sau
false.
Acest
tip de data este distinct si bine fundamentat, si nu poate fi convertit la voia
intinplari, ca si valorile similare din C si C++, in orice valoare numerica.
1.1.2.2.4 Operatori
logici si aritmetici
Operatorii
logici si cei aritmetici sunt cei tipici limbajului C de exemplu, si tocmai din
acest motiv nu vor fi discutati aicea. Totusi din cauza inexistentei tipurilor
de date fara semn, s-a introdus un operator aritmetic nou
“>>>”
care
satisface operatia de rotirea la dreapta a unei variabile de fãrã
semn (tip de data logic). Un alt operator la care I sa adãugat o
semnificatie noua este
“+”
care implementeaza, in cazul sirurilor de caractere operatia de concatenare.
1.1.2.2.5 Tablouri
Spre
deosebire de C, tablourile sunt in Java tipuri de date primare. Tablorile sunt
obiecte reale cu o reprezentare bine specificata in timpul rularii, ele se pot
aplica oricarui tip de data, si se pot aloca tablouri de tablouri pentru a
obtine tablouri multidimensionale. Declararea unui tablou se face de exemplu:
Point
myPoints[];
In
acest fel am declarat un tablou de obiecte de tip Point, clasa care am
definit-o in alta parte. Dar in acest moment avem numai un tablou
neinitializat, la care singura locatie de memorie alocata este cea a
handlerului tabloului (un handler este un fel de poiter, cu deosebirea ca
asupra lui nu se pot efectua operatii). In continuare trebuie alocat spatiu
suficient de stocare pentru tablou conform necesitatilor. Acesta se poate face,
simpu precum se poate vedea in exemplul urmator:
Astfel
sau alocat un spatiu de zece elemente, de tipul Point, dar care in acest moment
toate au valoare nula. Deci, deoarece avem alocat numai spatiu pentru obiectele
noastre trebuie create si obiectele, aceasta se poate face in modul urmator:
int
i;
for
(i = 0 ; i < 10 ; i++) {
myPoints[i]
= new Point();
}
Accesul
la elementele tabloului se face in mod identic cu accesul din limbajul C, dar
spre deosebire de C, tobloul este continu verificat pentru a nu depasi
dimensiunea maxima declarata si astfel sa corupa spatiul de memorie al altor
variabile sau date. In caz ca totusi sa depasit dimensiunea declarata, o
exceptie este generata, despre excepti, modul cum sunt tratate si declarate,
vom vedea in capitolele urmatoare. Faptul ca un tablou este un obiect de sine
statator, ii permite sa aiba propriile metode, astfel o metoda pe care o putem
aplica oricarui tablou este length() , metoda care permite determinarea
numarului de lemente dintr-un tablou. Metoda de implementare a tablourilor din
C a disparut, si odata cu ea si pointerii la tablou, cu ajutorul carora se
putea face o parcurgere a tabloului, pina la ultimul element si, din pacate
chiar si dupa, el ducind la unul dintr cele mai groaznice tipuri de erori, cele
intirziate, si care se pot manifesta dupa spatamini sau luni de rulare, si a
coror sursa este deosebit de greu de depistat, erori si tipuri de erori pe care
Java le-a eliminat coplet, prin mtodele de verificare si de constringeri (in
sens constructiv) pe care le impune.
1.1.2.2.6 Siruri
de caractere
Sirurile
de character sunt de asemena elemnte primitive ale limbajului, tip de obiect de
sine statator, si nu un simplu tablou de charactere ca si in limbajul C sau
C++. De fapt, Java are chiar doua tipuri de siruri de caractere, unul fiind
“
String”
care reprezinta siruri de caractere dar care sunt imuabile, pot fi numai
citite, (adica la orice operatie executata asupra sirului, de fapt se creaza un
sir de caractere nou, lasindu-se nemodificat cel vechi), iar celalat obiect
care reprezinta siruri de charactere fiind “
StrinBuffer”,
acesta opereaza direct asupra sirului initial, modificindu-l.
1.1.2.3. Managementul
memoriei si colectarea reziduurilor
In
aproape toate limbajsele de programare, managementul memoriei, alocarea,
gestionarea si eliberarea memoriei, revenea in sarcina programatorului, acesta
activitate ocupind timp si necesitind atentie sporita. Munca nu era usoara, a
tine minte tot timpul cind se poate elibra o anumita resursa, cind si mai ales
cum sa se gestioneze aceste resurse, pentru a nu se ajunge la situatia in care
programul, are nevoie de resurse, dar din pacate sunt ocupate, si nu
intotdeauna ar trebui sa fie. Pe linga sursa continua de stres pe care o
consituie, mai reprezinta sursa unui rau mult mai mare, scame de programare,
blocaje, scurgeri de memorie, si nu in ultimul rind performante slabe. Limbajul
Java preia complet presiunea acestei munci de pe umeri programatorului.
Pointeri in stilul limbajului C, aritmetica cu pointeri, malloc, si free nu
mai exista. Recuperarea automata a memoriei, “Garbage colection”,
ia preluat locul, facind parte integranta din mecanismul de rulare a
programelor Java. Desi exista o functie
new
pentru alocarea memoriei pentru diverse obiecte, Java nu are o functie
free
explicita. Obiectul odata creat de catre utilizator, mecanismul de management
al memoriei, preia controlul, urmarind tot timpul evolutia obiectului, in
momentul in care nu se mai face referire la obiectul respectiv, pe tot restul
programului, memoria ocupata de obiect este eliberata, permitind crearea a noi
obiecte in schimb.
Problema
care se pune in mod natural, este care sunt sacrificiile de performante pe care
le determina acest, sistem, care aduce avantaje in primul rind dezvoltatorilor
de aplicatii si nu neaparat clientilor. Ei pierderile de perforanta sunt minore
daca nu chiar nesemnificative, si toate acesta deoarece, sistemul Java
executiv, ruleaza managerul de memorie intr-un task separat, in fundal avind
prioritate foarte mica. Aceasta inseamna, deobicei atunci cind aplicatia nu
ruleaza in mod intens, cind se asteapta o reactie din partea utilizatorului,
deci deobicei cind procesorul nu este in cursul unei actiuni sau operatii.
Managerul de memorie mai resolva si un alt tip de probleme care de obicei
necesita, scheme de programe deosebit decomplexe. Prin alocarea succesiva, si
eliberarea de resurse de memorie, se produce un fenomen numit fragmentarea
memorie, fenomen care are ca efect negativ, imposibilitatea alocarii unei
anumite cantitati de memorie, pe care sistemul o raporteaza ca disponibila, dar
care din cauza ca, memoria disponibila nu se regaseste intr-o zona continua nu
o poate aloca. Managerul de memorie realizeaza si defragmentarea memoriei,
defragmentare care nu ar fi posibila, daca de exmplu s-ar di pastrat mecanismul
de pointeri atit de larg utilizati in limbajul C de exemplu. Prin mecanismul
de accesare a obiectelor implementat de Java, aceasta nu mai constituie o
problema, obiectele putind fi mutate dintr-o zona de memorie in alta,
modificindu-se adresa spre care directioneza handlerul obiectului.
1.1.2.4.
Mecanisme
de Multithreading si sincronizarea lor
Implementind
mecanismele de multithreading direct in limbaj, si nu numai la nivel de
librari, facilitatile oferite de diverse obiecte
Thread
precum si suportul oferit de catre executivul Java, ofera o usurinta,
performanta si simplitate greu de gasit la alte limbaje. Deasemena chiar daca
nu se construiesc aplicati cu mai multe fire de executie, simplitatea
mecanismelor de sincronizare permit implementarea aplicatilor de asa natura
incit trecerea lor la o aplicatie cu mai multe fire de excutie sa fie o
banalitate si sa prezinte un grad de securitate foarte mare .
1.1.2.5.
Elemente
eliminate din Limbaj
Printre
primele lucruri, avute in vedere in a nu fi implementate, a fost redundantele.
Este cunoscut modul in care mai multe functii si proceduri C sau C++, se
incaleca, existind prea multe metode de a raliza aceiasi functionalitate, care
in sine nu este neaparat negativ ci mai degraba inutil, si nerezolvare, sau
neoferirea de soluti pentru unele clase de probleme.
Un
alt element forte este eliminarea totala a elementelor de context si
preprocesare. Eliminind acestea sursele Java devin foarte simplu si facil e
inteles. Impatimitii limbajului C sau C++, i-si vor pune in mod natural
intrebarea, de ce, si cum se implementeaza in acest caz, anumite mecanisme.
Raspunzind
pe rind la intrebari, pentru a mentine simplitatea si claritatea codului. Avind
sursele unui program C sau C++, primul lucru care trebuie facut este studierea
headerelor, intelegera
defines
–urilor, si a
typedef
–urilor. Inainte de a parcurge aceste etape, programatorul nu are nici o
sansa de a intelege ce se intinpla in cadrul acelor surse. Si fiindca raul nu
vine nicioda singur, nu exista stiluri impuse pentru standardizarea acestor
sectiuni, fiecare programator avind propriul stil de a implementa aceste
elemente, cea ce contravine flagrant cu principiile unei programari de
calitate. Raspunzind la a doua intrebare, s-ar putea spune ca aceste mecanisme
nu se mai justifica. In Java efectul unui
#define
se poate obtine, folosind constate. Efectul unui #
typedef,
se optine creind, pur si simplu o clasa noua, dealtfel, orice clasa in Java
reprezinta un nou tip de data. Existenta fisierelor de header nu mai este
justificata pe de o parte de introducerea notiunii de pachete, pe de alta
parte din cauza stocarii a suficiente informatii in formatul binar la compilare
pentru toate tipurile de date folosite, pina la rulare cind se efectueaza si
operattia de linkeditare. Astfel programatorii pot citi si intelege codurile
mult mai usor nemaivorbind de partea mult mai importanta, modificarea surselor
de programe existente facilitind astfel refolosirea codului.
De
asemenea sau eliminat structurile sau multimile, deoarece o clasa asigura
aceiasi functionalitate. Pentru a demonstra acesta putem considera urmatorul
exemplu:
public
class Point extends Object {
double
x;
double
y;
//
urmeaza metodele de accesare a variabilelor
//
de instanta
}
Aceasta
bucata de cod defineste o clasa care reprezinta modelul unui punct in spatiu
bidimensional. Avind definitia unui punct putem defini structuri de date mai
complexe, cum ar fi un patrat. Definitia unei clase care sa surprinda
proprietatile unui patrat ar fi:
class
Rectangle extends Object {
Point
stingaSus;
Point
dreaptaJos;
//
metode de accesare a variabilelor de instanta
}
Acesta
s-ar reprezenta intr-un limbaj neorientat pe obiecte printr-o structura, in
Java ea este simplu definita ca o clasa. Un avantaj al clasei fata de structuta
este ca se pot specifica modificatori de acces la aceste variabile. Astfel,
fiecare din variabile de instanta pot fi facute
private,
publice
sau
protejate
dupa dorinta si necesitati permitind ascunderea sau afisarea dataliilor de
implementare a obiectelor instantiate din aceste clase.
Un
element care intentionat nu s-a inclus in limbajul Java au fost functiile. Un
model orientat pe obiecte, este mult superior functional unui model procedural.
Combinarea celor doua stiluri, nu ar duce decit la confuzie si ar dilua
puritatea unui limbaj orientat pe obiecte. Toata functionalitatea obtinuta
printr-o functie poate fi la fel de bine obttinuta definind o clasa si
construindui metodele necesare. Considerind exemplul anterior, ne propunem sa
construim metodele de acces la variabilele de instanta.
public
class Point extends Object {
private
double x;
private
double y;
public
void setX (double x) {
this.x
= x;
}
public
void setY (double y) {
this.y
= y;
}
public
double x() {
return
this.x;
}
public
double y() {
return
this.y;
}
}
Se
poate observa ca variabile x si y, sunt declarate ca fiind private, acesta
permite ascundera detaliilor de implementare, ele putind fi accesate numai prin
intermediul metodelor de acces. In acest mod, modul de implementare intern pe
parcursul timpului poate fi modificat, programatorul care utilizeaza aceasta nu
poate face apel la nimic ce este declarat privat, el comunicind cu clasa prin
cele patru metode puse la dispozitie, aplicindu-le modificatorul
public.
Un
alt mecanism care nu se regaseste in Java, si care de acesta data, este de
domeniul programarii orientate pe obiecte este mostenirea multipla. Un mecanism
similar fiind intrrodus, pentru a permite numai avantajele mostenirii multiple
sa se implice in Java nu si dezavantajele ei. Mecanismul este similar cu cel
inplementat in Objective C, si poarta denumirea de interfete.
O
interfata nu este o definitie a unui obiect, ci mai degraba o definitie a unui
set de metode pe care acel obiect urmeaza sa le implementeze. Un aspect
interesant al interfetelor este ca ele pot declara numai metode sau constante
si nici intr-un caz variabile.
Concluziile
par a fi clare, Java este:
Simplu
–
deoarece numarul structurilor care trebuie invatat si intelese pentru a realiza
ce ne-am propus este minim
Familiar
-
deoarece Java arata ca si C sau C++, exceptind bineinteles partile confuze ale
acestor limbaje care au fost eliminate.
Tehnologii
avansate de programare, implementate de Java cu potential in automatizari
1.2.1. Programarea
orientata pe obiecte
Pentru
a rezista in fata limbajelor de programare din ziua de azi, Java este construit
de la baza in ideea programarii orientate pe obiecte. Paradigma orientari pe
obiecte, acopera foarte bine necesitatile programelor client-server sau
aplicatiilor distribuite. Din pacate , termenul de orientat pe obiecte ramine
putin inteles, fiind de obicei considerat leacul minune care rezolva orice
problema, si transforma aplicatiile prost concepute in aplicatii bune.
Versiunea cinica asupra orientarii pe obiecte afirma ca este numai o alta
metoda de a organiza datele, si desi este o portiune de adevar in aceasta
afirmatie, ea nu acopera totul, realitatea fiind ca sunt lucruri care folosind
metodologia programarii pe obiecte pot fi implementate si care prin metodele
standard, procedurale, sunt imposibil de realizat.
Pentru
a putea vorbi de programare orientata pe obiecte trebuie amintite elementele
sau facilitatile care stau la baza ei. Deci caracteristicile de baza a
orientarii pe obiecte sunt:
- Incapsularea
–
prevede
ascunderea datelor, si permite abstractizarea si modularizarea datelor
- Polimorfismul
–
prevede ca acelasi mesaj, trimis obiectelor diferite provoaca din partea lor un
comportament individual diferit
- Mostenirea
–
prevede facilitatea de a extinde o clasa de baza, clasa derivata mostenind
toate structurile de date si metodele clasei de baza, facilitind in acest fel
re utilizarea codului, si o organizare mai buna a surselor.
- Legarea
dinamica –
obiectele pot proveni teoretic de oriunde, local sau de undeva de pe o retea ,
deci se impune necesitatea de a putea trimite mesaje, obiectelor fara a fi
nevoiti sa cunoastem exact tipul lor la momentul scrierii programului. Astfel
legarea dinamica permite maximul de flexibilitate in timpul executiei unui
program Java
Ideea
de baza, este organizarea programelor de asa maniera, incit sa reflecte,
obiectele din lumea reala.
1.2.1.1.
Definitia
obiectelor
Exprimat
simplist, tehnologia orientata pe obiecte este o colectie de metodologii de
analiza, proiectare si programare care se concentreaza pe modelarea
caracteristicilor si comportamentului obiectelor din lumea reala. Definitia
data pare a fi una tautologica, asa ca o descriere mai naturala poate fi
formulata in felul urmator: sunt modele software de programare.
In
viata de zi cu zi suntem inconjurati in permanenta de obiecte: masini, copaci,
tot felul de automate, etc. Aplicatiile software contin de asemenea obiecte cum
ar fi: butoane pe interfata cu utilizatorul, tabele, celule de tabele, meniu,
si multe altele. Aceste obiecte se pot caracteriza prin o stare si un
comportament. Toate acestea se pot modela prin componente software, care la
rindul lor se pot caracteriza prin stare si comportament.
Orice
element din lumea care ne inconjoara, fie element natural, fie artificial poate
fi modelat ca un obiect. De exemplu, o turbina poate fi modelata ca un obiect
avind ca elemente de stare, dimensiunea, numarul de palete, turatia cu care se
invirt, iar ca comportament, se poate spune, ca o turbina poate fi pusa in
functionare, poate fi oprita, se poate defecta, si asa mai departe. Un alt
exemplu am putea pe care putem sa-l consideram, ar fi un regulator, atit de
folosit in automatizari, astfel se poate spune ca are ca stare, valoare
semnalului curent, iar ca si comportament, genereaza un semnal, preia un
semnal, eventual porneste o alarma.
1.2.1.2.
Fundamentele
obiectelor
In
implementarea practica a unui obiect, starea unui obiect se poate traduce prin
variabilele de instanta. Aceste variabile de instanta sunt definitorii pentru
un obiect. Comportamentul unui obiect se poate implementa prin intermediul
metodelor. Metodele actioneaza asupra variabilelor de instanta modificind in
acest fel starea obiectului, de asemenea metodele unui obiect pot crea la
rindul lor alte obiecte.
Fig.
1.2.1.2.1 Reprezentarea unui obiect
In
figura 1.2.1.2.1 se poate observa reprezentarea schematica a unui obiect.
Diagrama reprezinta structura conceptuala a unui obiect, ca o remarca, se poate
face analogie cu o celula, cu membrana exterioara, re4prezentata in exemplul
nostru de catre metode, si cu un nucleu inconjurat de catre membrana care il
protejeaza. Variabilele de instanta, reprezentind datele, starile obiectului,
sunt impachetate, incapsulate, in interiorul obiectului fiind inconjurate de
metode. In mod normal, metodele sunt singurele mijloace prin care se poate
modifica continutul variabilelor de instanta, a datelor. Dar in interiorul
clasei se poate declara o anumita variabila de instanta ca fiind publica si
atunci, continutul ei poate fi modificat si in mod direct.
1.2.1.3.
Clasele
O
clasa este o componenta software care defineste variabilele de instanta si
metodele obiectului respectiv. O clasa in sinea ei nu este un obiect ci poate
fi privit mai degraba, ca un model pentru creare obiectelor. Obiectele se pot
obtine prin operatia de instantiere a unei clase gata definite. Nu exista
restrictii privitor la numarul de obiecte care pot fi instantiate dintr-o clasa.
O
declaratie foarte simplista a unei clase generice care modeleaza un regulator
ar putea fi:
public
class Regulator extends Object {
double valoareSemnal;
public
double valoareReferinta;
//
metode de acces la variabilel de instanta
}
Avind
definita clasa care modeleaza un regulator, orice obiect din cadrul programului
nostru i-si poate crea o instanta sau mai multe din Regulator (deci i-si poate
crea mai multe obiecte Regulator).
Modul
cum s-ar realiza aceasta este exemplificat mai jos:
Regulator
regulatorulPentruPresiune1;
//
sa declarat o variabila prin care se va face
//
referire la regulatorul nostru
regulatorulPentruPresiune1
= new Regulator();
//
se aloca memorie si se instantiaza obiectul
//
nostru.
Acum
se poate accesa obiectul definit si instantiat prin specificarea variabilei de
instanta pe care dorim sa o accesam, sau metoda daca nu dorim variabila,
precedate de numele instantei (obiectului) nostru, proaspat alocat.
ReghulatorulPentruPresiune1.valoareReferinta
= 1;
Am
putut sa accesam direct, variabila de instanta,
valoareReferinta
tocai deoarece ea a fost definita in cadrul clasei Regulator, ca fiind publica.
In schimb valoare variabilei
valoareSemnal
nu poate di accesata decit in cadrul pachetului curent din care apartine si
clasa Regulator.
1.2.1.4.
Constructori
Cind
se declara o clasa, exista posibilitatea sa se declare optional un constructor,
care sa efectueze o initializare la instantierea unui obiect din acea clasa. De
asemenea se poate declara un finalizator, care este executat inainte de
distrugerea obiectului. Aceasta ar fi o facilitate foarte utilizata in
automatica, unde de obicei se lucreaza cu echipamente hard, care de obicei tot
timpul trebuie initializate, inainte de a lucra efectiv cu ele. Aplicind cele
discutate aicea se poate da un exemplu de construire a unui constructor, chiar
pentru clasa noastra de Regulator.
Class
Regulator extends Object {
double valoareSemnal;
public
double valoareReferinta;
//constructorul
care realizeaza initializarea valorilor
public
Regulator() {
valoareSemnal=
citesteSemnal();
valoareReferinta=
1;
}
//constructor
care initializeaza la anumite valori
public
Regulator(double referinta) {
valoareSemnal=
citesteSemnal();
valoareReferinta=
referinta;
}
}
Metode
cu acelasi nume ca si clasele se numesc constructori. La instantierea unui
obiect primul lucru care se executa este constructorul, de altfel noi nu suntem
obligati sa furnizam obligatoriu un constructor, deoarece compilatorul, in caz
ca nu gaseste nici un constructor, fabrica unul. Prin executarea
constructorului, se da posibilitatea efectuari diverselor operatii de
initializare, precum si operatia de initializare a variabilelor cu valorile
dorite.
Astfel,
un exemplu de instantierea a unui regulator, cu valoare de referinta, diferita
de 1, se poate face acum, foarte usor:
Regulator
regul1;
Regulator
regul2;
regul1
= new Regulator(); /* primul constructor */
regul2
= new Regulator(0.5); /* cel dea-l doilea */
1.2.1.5.
Metode
si mesaje
Daca
un obiect, doreste, sa-i comunice unui alt obiect ca e cazul sa-si modifice
starea, atunci in jargonul programarii orientate pe obiecte, primul obiect ii
trimite un
mesaj
celui de-al doilea obiect. Ca reactie cel de al doilea obiect selecteaza o
metoda potrivita pe care sa o apeleze. Dealtfel invocarea metodelor seamana
destul de mult cu a functiilor din C si C++.
Fig:
1.2.1.5.1 Modul de comunicatie intre obiecte
Folosind
paradigma programarii orientate pe obiecte se pot construi, adevarate retele si
pinze de obiecte schimbindu-si starea in urma comunicatiei intre ele prin
mesaje. Aceasta tehnica de programare este una dintre cele mai propice pentru a
crea modele care sa simuleze sisteme deosebit de complexe din lumea reala.
Revenind la modelul nostru de regulator putem sa-cream metodele, pentru a
permite altor obiecte sa comunice cu el:
public
class Regulator extends Object {
double
valoareSemnal;
double
valoareReferinta;
public
Regulator() {
valoareSemnal=
0;
valoareReferinta=1;
}
public
void setValoareReferinta(double ref) {
valoareReferinta
= ref;
}
public
double getValoareReferinta() {
return
valoareReferinta();
}
public
void readSignal() {
}
public
double getValoareSemnal() {
return
valoareSemnal;
}
}
Acum
variabilele de instanta nu mai sunt accesibile din exterior si deci singura
metoda de a le accesa ramine prin intermediul metodelor puse la dispozitie de
interfata obiectului. O comunicare cu obiectul poate avea urmatorul scenariu:
Regulator
reg1 = new Regulator();
reg1.setValoareReferinta(0.5);
double
valoarecitita= reg1.getValoareReferinta();
A
face variabilele de instanta, publice sau private, este la latitudinea
programatorului. Facind variabilele publice se descopera unele dintre detaliile
de implementare, din aceasta cauza asigurind o eficienta si rapiditate mai
mare, platind in schimb, pretul in viitor , la intretinerea clasei unde
detaliile de implementare vor putea fi greu modificate tocmai din cauza ca au
fost publice si folosite ca atare. Ascunzind detaliile de implementare al unei
clase, transformindu-i toate variabilele de instanta in variabile de tip
privat, se asigura pe viitor toate conditiile unor schimbari radicale, oarece
utilatori nu au avut acces direct la acele variabile, ci numai prin intermediul
unor metode de acces, care se pot pastra.
1.2.1.6.
Finalizatori
De
asemenea la fiecare clasa se poate defini un finalizator, acesta se face
definind efectiv o metoda publica
finalize().
Functionalitatea unui finalizator este de fapt explicata si de numele ei. In
momentul, in care un obiect urmeaza a fi dealocat, distrus de catre managerul
de memorie, acesta apeleaza, daca exista, pentru toate obiectele metoda
finalize, deci acesta este locul propice pentru a elibera anumite resurse, sau
a executa anumite operati de terminare. Astfel de exemplu, daca la nivel
superior avem o clasa care defineste un obiect care controleaza un intreg
proces, la terminare se poate opta pentru, oprirea fiecarui sub proces si
oprirea in siguranta a intregului proces. Si toate aceste executindu-se in mod
natural.
1.2.1.7.
Mostenirea
Mostenirea
este un mecanism, prin care noi obiecte, cu facilitati extinse se pot defini pe
baza unor obiecte existente. Ideea este deosebit de frumoasa cu avantaje atit
in modul de concepere al aplicatiilor, permitind o organizare frumoasa si
eficace a datelor, precum si o metoda deosebit de utila de re utilizare a
codului. Se poate scrie de exemplu o clasa generala, care sa implementeze marea
majoritate a codului, cod care este independent de sunctionalitatile specifice
ale obiectelor, iar pe urma, mostenind din aceasta clasa de baza,se pot realiza
obiecte cu functii diverse, dar cu o cantitate de cod scrisa cu cel putin 50%
mai putin. Un astfel de exemplu ar putea fi chiar un regulator, si metodologia
ar putea fi urmatoarea: se realizeaza o clasa care sa implementeze un regulator
de tip P, prin mostenire se poate crea o clasa noua care sa implementeze un
regulator PI, singurul lucru pe care trebuie sa-l implementeze noua clasa este
o metoda noua numita
reglareI,
si
sa se suprascrie metoda generala care implementeaza reglare, sa spunem
reglare,
metoda care sa contina atit apelul de
reglareP,
pe care i-l avem, in noua noastra clasa, fiind cel mostenit din clasa
RegulatorP,
la care mai adaugam si apelul metodei noi implementate numite
reglareI
, si astfel avem, cu un efort minim un nou regulator, care stie sa implementeze
legea de reglare PI. Se poate merge si mai departe, din clasa obtinuta, sa zicem
RegulatorPI
putem mosteni o noua clasa numita
RegulatorPID
care mai trebuie sa implementeze metoda care sa asigure reglarea de tip D. Deci
cu un efort minim, am reusit sa cream trei tipuri de obiecte pe deplin
functionale. Potentialul pe care-l prezinta aceasta metodologie nu se opreste
aicea, asigurindu-se facilitati impresionante.
Fig:
1.2.1.7.1 O ierarhie de clase
Mostenirea
deci permite folosirea codului existent, si cel mai important gata testat,
pentru o diversitate de probleme. Prin definirea de metode sau variabile noi
se adauga functionalitati noi, iar prin supradefinirea metodelor din clasa de
baza se modifica comportamentul viitorului obiect, comportament mostenit de la
parinte. Partea cea mai interesanta, este ca obiectul fiu, poate fi folosit in
cadrul programelor de parca ar fi parinte. Aceasta tehnica se numeste
downcasting,
si se bazeaza pe faptul ca toate metodele pe care le are clasa de baza
(parintele), prin mostenire le are si copilul, deci nu este gresit, sa-l
folosim, ce se obtine de pe urma acestei metode poarta numele de polimorfism.
Avantajele sunt imense, bunaoara, reluind exemplul cu regulatorul, noi nu
trebuie sa stim exact, ce tip de regulator vom folosi la reglare, ci este
suficient sa lucram cu clasa de baza. Iar la rulare putem sa instantiem orice
tip de regulator, si sa-i atasam handlerului regulator, handlerul
regulatorului pe care i-l dorim. In acest mod, de exemplu putem sa schimbam
foarte simplu legile de reglare pe care le vom folosi, in functie de ce handler
vom pasa, handlerul unui regulator P, PI sau PID.
Considerind
exemplul, amintit, implementarea lui ar fi:
public
Regulator extends Object {
//
declararea variabilelor de instanta
//
declararea constructorilor
public
void regleaza() {
}
}
Acum
implementam regulatoarele propriuzise, care mostenesc proprietatile clasei de
baza
Regulator.
public
RegulatorP extends Regulator {
//declararea
de variabile specifice regulatorului P
//
restul fiind mostenite din clasa de baza
public
void legeRelgareP() {
//
implementarea legii;
}
//supradefinim,
metoda regleaza, pentru a lua in evidenta
//legea
de reglare
public
void regleaza() {
legeReglareP();
}
}
Regulatorul
PI, il mostenim din regulatorul P, pentru a nu mai fi nevoiti sa-l implementam
public
RegulatorPI extends RegulatorP {
//declararea
de variabile specifice regulatorului I
//
restul fiind mostenite din clasa de baza
public
void legeRelgareI() {
//
implementarea legii;
}
//supradefinim,
metoda regleaza, pentru a lua in evidenta
//legea
de reglare
public
void regleaza() {
legeReglareP();
legeReglareI();
}
}
Folosirea
mostenirii si a polimorfismului se exemplifica mai, jos. Presupunem ca avem un
proces, si avem o metoda
regleazaProces(Regulator
reg)
care efectueaza reglarea procesului:
Regulator
reg
= new Regulator();
RegulatorP
regP
= new RegulatorP();
RegulatorPI
regPI= new RegulatorPI();
//
se lanseaza reglarea procesului cu un regulator P
regleazaProces(((Regulator)regP));
//
se poate observa, ca se face o trensformare de tip, din
//
RegulatorP in Regulator, acesta deoarece, am construit
//
metoda regleazaProces, sa poata lucra generic cu orice
//
tip de regulator
//
se lanseaza reglarea procesului cu un regulator PI
regleazaProces(((Regulator)regPI));
Desi,
sa facut un downcasting, metodele noi definite in clasa RegulatorP, nu se
pierd, mecanismul executiv, sesizeaza ca e vorba de o clasa derivata, din clasa
Regulator, si apeleaza metoda, corespunzatoare din clasa care trebuie si anume
RegulatorP, sau RegulatorPI.
1.2.1.8.
Controlul
accesului
La
declararea unui clase se poate specifica nivelul de acces permis la variabilele
de instanta, sau metodele clasei respective. Java permite patru nivele de acces
dintre care trei trebuie specificate explicit, unul fiind implicit. Acestea
sunt: public, protected, si private. Cel de-al patrulea nivel nu are o denumire
propriuzisa, folosindu-se de obicei denumirea de friendly, si este specificator
de acces care este folosit in mod normal daca nu se specifica nici un
specificator explicit. Drepturile pe care le garanteaza, sunt cele de acces
nelimitat la variabilele de instanta si la metode, pentru toate clasele care
fac parte din cadrul aceluiasi pachet, si total inaccesibile pentru restul
claselor. Un pachet, fiind metoda implementata de limbajul Java pentru,
gruparea mai multor clase. Specificatorul friendly, este deosebit de util, daca
se lucreaza, la elaborarea unui pachet care contin clase (obiecte),
interconectate functional. Un astfel de exemplu ar putea fi implementarea unui
pachet de regulatoare, si din acest punct de vedere, variabila de instanta,
semnalReferinta, ar putea fi accesata in mod direct de toate clasele din cadrul
pachetului, cea ce este logic. [wp01] De asemenea in afara pachetului, care
contine definitiile regulatoarelor, aceasta variabila de instanta va putea fi
accesata numai prin intermediul metodelor speciale de acces concepute in acest
sens. Aceasta abordare permite, printre altele, modificarea ulterioara a
modului cum vor fi implementate ulterior aceste regulatoare, in ideea ca pe
parcursul timpului, noi metodologii, si tehnici apar, iar clasele care
implementeaza regulatoarele pot fi actualizate, si toate acestea fara a afecta
programele care apeleaza, clasele din aceste pachete, deoarece interfata ramine
neschimbata, modificind-se numai detaliile implementari. Avantajele fiind
evidente. Specificatorul de acces public semnifica, ca metodele si / sau
variabilele de instanta sunt accesibile de oriunde si pentru oricine, protected
asigura accesul doar a elementelor din aceeasi clasa, sau din clasele derivate
din acea clasa. In final specificatorul de acces private interzice accesul
tuturor exceptind accesul din propria clasa
.
1.2.1.9.
Metode
si variabile de clasa
Java
urmeaza conventiile limbajelor orientate pe obiecte, asigurind astfel suport
pentru variabile si metode de clasa. In mod normal toate variabilele sau
metodele dintr-o clasa, sunt variabile de instanta, adica ele sunt distincte
pentru fiecare obiect instantiat din clasa respectiva, pe de alta pare metodele
si variabilele de clasa sunt unice pentru clasa respectiva, aceasta insemnind
ca indiferent cite instante se creeaza din clasa respectiva, acele metode sau
variabile vor fi unice pentru toate instantele, toate obiectele impartind
aceleasi metode sau variabile. Pentru a declara variabilele in acest fel ele
trebuie declarate
static,
ca in exemplul urmator:
Class
Regulator extends Object {
...
static
int semnalReferinta;
...
}
Prin
aceasta manevra, bunaoara indiferent cite regulatoare am avea instantiate,
modificind aceasta variabila, modificarea se propaga automat la toate. Daca am
dori ca valoarea lor sa nu poata fi modificata, putem adauga modificatorul
final.
Ideea pentru care au fost implementate metodele de clasa, este frecventa cu
care se obisnuieste sa se foloseasca instantierea unui clase in mai multe
obiecte, care au toate aceiasi metoda, care satisface aceiasi functionalitate.
Acest obicei are ca rezultat o risipa de resurse, care de multe ori trebuie
dramuite cu multa atentie. In astfel de cazuri, metodele de clasa rezolva
problema. Singurele restrictii care se impun metodelor de clasa este ca ele nu
pot accesa variabile de instanta sau metode normale, ci numai variabile sau
metode care la rindul lor sunt de clasa.
1.2.1.10. Metode
abstracte
Metodele
abstracte prezinta o facilitate puternica a programarii orientate pe obiecte.
Pentru a intelege metodele abstracte, si functionalitatea lor, pornim de la
ideea de superclasa abstracta. O superclasa abstracta este o clasa in care se
definesc un numar de metode, dar care nu sunt implementate efectiv de catre
clasa, ci au rolul de a sigura numarul, numele, parametri si alte aspecte, care
pe urma vor fi suprascrise, de catre clasele care vor mosteni din acea clasa
abstracta. Cu alte cuvinte o clasa abstracta are menirea de a simula un obiect
abstract, obiect care reuseste sa generalizeze un set de proprietati si
comportamente ale unei clase de obiecte. Dintr-o clasa abstracta nu se vor crea
niciodata obiecte, ci se vor mosteni clase care pe urma vor fi instantiate. Un
exemplu de implementare, ar fi reglarea unui proces. Orice proces care trebuie
reglat se presupune ca contine un regulator, dar ideea optimala de implementare
este de a nu ingloba regulatorul direct in aplicatie, (insemnind, efort
considerabil, in caz ca modelul de regulator folosit nu asigura performantele
necesare, si se impune schimbarea modelului). Ci de a sintetiza
caracteristicile unui regulator, si de a folosi, imaginea abstracta a unui
regulator in cadrul aplicatiei, imagine din care se pot crea implementari
efective de regulatoare, implementari care vor putea fi folosite fara probleme,
deoarece implementeaza toate caracteristicile modelului abstract. In acest mod
se pot experimenta modele de regulatoare diferite, sau se poate modifica
modelul de regulator, fara a modifica structura aplicatiei.
Fig:
1.2.1.10.1 Utilitatea unei clase abstracte
Utilizind
aceasta functionalitate in cadrul clasei abstracte, se implementeaza, toate
metodele care nu sunt direct conectate sau dependente de legea de reglare
utilizata. In momentul in care se mosteneste un nou regulator, acesta va trebui
sa supradefineasca doar metoda care realizeaza efectiv implementarea legii de
reglare, restul metodelor fiind implementate in clasa abstracta, devin
disponibile si functionale. Din cite se poate observa, pe linga faptul ca se
asigura o interfata comuna si unitara regulatoarelor, se introduce o
simplificare claritate si ordine in procesul de implementare a lor uimitoare.
1.2.1.11. Sumar
Acest
subcapitol a prezentat aspectele esentiale ale tehnologiei orientate pe
obiecte, aceste facilitati fiind:
- Clasele
definesc modele de obiecte, modele care prin instantiere devin obiecte
concrete, care posed stari si comportamente
- Obiectele
comunica intre ele prin intermediul
mesajelor.
Obiectele raspunzind unui mesaj prin intermediul unei metode executate.
- Metodele
definesc comportamentul obiectelor instantiate dintr-o clasa. In mod contrar
limbajelor procedurale, un obiect poate avea metode cu acelasi nume, ca si
metodele altor obiecte. Un obiect dat poate raspunde unui mesaj intr-un mod
determinat de natura sa, demonstrind un comportament polimorfic.
-
Mostenirea
asigura
facilitatile prin care un nou obiect poate mosteni variabilele de instanta si
metodele de la alte obiecte gata definite. Clasa noua definita poate adauga la
rindul ei noi metode (adauga noi comportamente) sau variabile de instanta
(adauga noi starilor), sau poate suprascrie metodele clasei parinte
(modifcindu-I in acest fel comportamentul).
Programarea
orientata pe obiecte, lucreaza cu conceptul de obiect, concept folosit in mod
natural de oameni, in viata, astfel se transpune un mod natural de gindire si
in activitatea de programare, procesul fiind mult simplificat. In automatizari
se lucreaza cu obiecte reale, fizic existente si astfel folosirea unui limbaj
de programare orientat pe obiecte este cea mai naturala alegere posibila, si
cea mai fidela in modelarea comportamentului diverselor obiecte, cu atit mai
mult cu cit obiectele sunt fizic reale, si o analiza din acest punct de vere
este mult usurata, rezultatele fiind mult mai fidele.
Multithreading
in Java
Atit
programatori cit si utilizatorii, au inceput in ultima perioada sa fie
nemultumiti de maniera calculatoarelor personale de a face un singur lucru de
odata. Observind ca in lumea reala din jur o multitudine de lucruri se intimpla
simultan, apare dorinta fireasca ca si calculatoarele sa functioneze in acelasi
mod. Din pacate a scrie programe care sa gestioneze mai multe lucruri care se
intimpla simultan este mult mai dificil decit a scrie programe conventionale
care lucreaza cu un singur fir de executie. Aplicatii cu mai multe fire de
executie se pot scrie si in limbaje precum C, C++ sau ADA, dar complexitatea
tehnica a unor implementari de genul acesta este mai mare cu cel putin un ordin
de marime, si chiar daca se reuseste, nu este nici o garantie ca producatorii
de componente software cu care eventual se lucreaza sau se intentioneaza a se
lucra, au implementate acele componente astfel incit sa suporte, lucrul cu mai
multe fire de executie adica sa manifeste afinitate multithreading. Termenul de
afinitate multithreading, insemnind ca acele librarii sau functii sunt
implementate de asa maniera, incit sa permita executia de simultana de catre
mai multe fire de executie. Problema majora cu mediile care ofera suport
explicit pentru programarea multi fir, este ca nu prezinta niciodata garantii
ca s-au realizat lock-urile necesare, si ca s-au eliberat exact in momentul
potrivit. De exemplu daca s-a iesit prematur dintr-o metoda, sau s-a generat o
exceptie, si lock-ul nu a fost eliberat, rezultatul obisnuit obtinut in
asemenea cazuri este interblocajul.
1.2.2.1.
Threaduri
la nivel de sintaxa
Suportul
integrat in limbajul Java pentru multithreading, il transforma intr-o unealta
deosebit de puternica pentru diverse operatii, incepind de la cele de natura
tehnica, supraveghere mai multor procese sau parametri pina la imbunatatirea
performantelor aplicatiilor grafice intensive. Ambele tipuri fiind puternic
prezente in aplicatiile de automatizari. Threadurile au fost considerate o
piatra de hotar deosebit de importanta pentru Java. Astfel librariile de baza
asigura o clasa Thread care implementeaza o bogata colectie de metode pentru a
porni, opri, rula, verifica un fir de executie. De asemenea sa inclus un
mecanism sofisticat de sincronizare bazat pe paradigma larg folosita de
monitorizare si variabile conditionate, introdus de catre C.A.R. Hoare si
implementat pentru prima data de catre compania Xerox la sistemele Cedar/Mesa .
Multe articole din domeniul informatici trateaza subiectul programarii
concurente. Concurenta fiind subiect de cercetare de o lunga perioada de vreme,
o multitudine de solutii au fost propuse si implementate. O parte din
rezultatele acestor cercetarii sunt, urmatoarele metode:
- Sectiuni
critice
- Semafoare
- Mutex
- Monitoare
Principiul dupa care functioneaza este cel al unui obiect specializat, denumit
monitor,
care aplica principiul excluderii mutuale aplicat pe grupuri de proceduri.
Monitoarele Java forteaza excluderea mutuala a accesului la metode, mai
specific se forteaza
excluderea mutuala la metodele sincronizate. Unde cuvintul cheie
synchronized
este
un modificator de metode optional
[ju01].
Threadurile
Java sunt
pre-emptive,
si
depinde de platforma pe care masina Java
ruleaza, astfel ele pot fi si de tip
time-sharing.
Pe
cind celelalte limbaje asigura facilitati de multithreading de obicei prin
intermediul unor librarii, de thread-uri, suportul inglobat chiar in miezul
limbajului asigura programatorii cu unelte mult mai puternice si facile pentru
crearea aplicatiilor cu afinitate pentru mai multe fire de executie sau chiar
cu mai multe fire
de
executie. Un alt avantaj al multithreading-ului este un mai bun raspuns, sau o
mai mare interactivitate si comportament de timp real bun.
Mecanismul
implementat de catre Java, intern pentru gestionarea proceselor asigura in mod
unic programelor Java rulind sub diferite platforme performantele de timp real
native platformei respective.
Efectiv
in interiorul masinii virtuale, multitasking-ul e implementat ierarhizat,
organizat pe mai multe nivele. La nivelul cel mai ridicat se situeaza un set de
metode abstracte independente de platforma, iar la nivelul cel mai inferior, un
nucleu mic de rutine dependente de platforma [TJ01].
Fig:
1.2.2.1.1 Arhitectura threadurilor Java
1.2.3. Tratarea
exceptiilor
Erorile
sunt, din pacate parte a programelor. Unele dintre aceste erori sunt probleme
de proiectare restul fiind de implementare, fiind denumite in limbajul curent
scame, sau bug-uri. Un alt tip de erori, care nu sunt considerate same, se
datoreaza unor situatii precum, memorie insuficienta sau nume de fisiere
gresite. Modul cum sunt tratate ultimele tipuri de probleme determina daca
acestea se transforma in scame sau nu. Din pacate daca telul este de a produce
aplicatii robuste, cum este cazul de exemplu si in automatizari, unde
aplicatiile nu au voie sa piarda controlul, altfel totul al putea sa se
finalizeze intr-o catastrofa, vom descoperi ca vom petrece mai mult timp
tratind erori decit efectiv scriind cod pentru aplicatiile noastre.
Mecanismele
de tratare a erorilor implementate de limbajul Java, permite sa tratam erorile
fara a forta programatorul sa-si epuizeze energiile tratindu-le sau facindu-si
griji din cauza lor.
1.2.3.1.
Ce
sunt exceptiile
Asa
cum sugereaza si numele o exceptie este o conditie exceptionala, ceva iesit din
rutina obisnuita, sau ca un termen care se poate folosi in antiteza cu bine.
Mecanismul exceptiilor asigura nu numai o modalitate de semnalizare a erorilor,
dar si o metoda de a le trata. Aceasta noua structura permite sa se specifice
exact unde sa se trateze anumite tipuri de erori.
Desi
tratarea erorilor nu este o particularitate numai a limbajului Java, existind
tratare de erori si in limbajele C, C++ sau Ada, cea ce diferentiaza, Java de
restul limbajelor este ca pe cind tratarea erorilor in restul limbajelor e
optionala, lasata la latitudinea programatorului, in Java ea este impusa. Daca
o anumita metoda este susceptibila de a genera o anumita
eroare,
atunci daca nu este tratata, este considerata o eroare de catre compilator, si
compilarea este oprita. De fapt functiile de nivel scazut (implementate de
catre Java) detecteaza erorile pe cind, functiile situate pe nivele superioare
(implementate de programator in cadrul aplicatiei) determina ce se va intimpla
in cazul aparitiei erori, exceptiile asigurind o metoda de comunicatie a
informatiilor legate de erorile aparute in sus pe lantul de metode, pina cind
se gaseste o metoda care sa stie sa trateze eroarea respectiva (vezi [MM]).
Exceptiile pot fi privite ca o structura de control nonlocala . Cind o metoda
genereaza o exceptie, apelantul metodei trebuie sa determine daca este capabil
sa capteze exceptia sau nu. Daca poate sa o capteze, atunci preia controlul
executiei programului, daca nu este capabil atunci exceptia este data mai
departe metodei apelante, si acest mecanism continua, pina cind una din metode
este capabila sa prelucreze exceptia, sau in caz ca nu exista o astfel de
metoda, executia programului se intrerupe, deoarece exceptia nu a fost prinsa.
Toate exceptiile Java mostenesc clasa
java.lang.Throwable,
deoarece toate exceptiile sunt obiecte ele pot incapsula, atit date cit si
metode, de altfel chiar clasa de baza implementeaza o metoda care returneaza un
sir de caractere continind descrierea erori, care a generat exceptia, acesta
fiind deosebit de util in operatia de depanare.
Captarea
unei exceptii se face cu ajutorul unui bloc inceput cu cuvintul cheie
try,
si finalizat cu o succesiune de secvente de
catch-uri
structura reprezentata mai jos.
try{
...
//instructiuni
susceptibile de a genera erori
...
}
catch ([tip_exceptie1] ex) {
//
secveta tratare acest tip exceptie
}
catch ([tip_exceptie2] ex) }
//
secventa tratare tip_exceptie 2
}
catch (Throwable th) {
//
deoarece toate exceptiile provin din aceasta
//
clasa e bine, daca dorim sa prindem toate
//
exceptiile ca la capat sa o folosim pe cea mai
//
generala posibila
}
Din
cite se poate observa se poate face o filtrare dupa, exceptii deci puntem trata
selectiv exceptiile generate.
Nu
toate metodele trebuie sa capteze erorile, ele avind optiunea sa le transmita
mai departe, una dintre cele doua optiuni trebuie aleasa, aceasta din cauza ca
limbajul impune tratarea tuturor exceptiilor. O metoda care nu trebuie sa fie
incarcata cu tratare de exceptii poate opta sa le transmita mai departe, la
ierarhul sau, faptul ca o metoda poate genera o exceptie se specifica prin
intermediul modificatorului
throws,
astfel dupa declaratia throws-ului este neaparat necesar sa se enumere
tipurile de exceptii pe care le poate genera, astfel prototipul unei metode
care genereaza exceptii se poate defini:
void
metoda_generatoare_de exceptii ([parametri])
hrows
IOException, [alte_exceptii] {
...
//
corpul metodei
...
}
Tipul
de data returnat de metoda nu este neaparat nevoie sa fie
void
putind fi orice tip de data. Limbajul Java mai adauga o clauza tratarii
exceptiolor, clauza, impusa de nevoile practice, si anume blocul
finally,
deci structura completa, de tratare a exceptiilor este:
try{
//secvente
de instructiuni
}
catch ([tip_exceptie])
//
tratarea exceptiei
}
finally {
//secvente
de instructiuni
}
Ultimul
bloc din structura de mai sus, are menirea de a asigura o structura care sa se
execute tot timpul, chiar si cint sunt generate exceptii. Un astfel de loc, de
exemplu este folosit pentru inchiderea conexiunilor deschise, sau a fisierelor.
In
Java se poate face diferenta intre doua clase, exceptiile clasice si erori.
Amindoua clasele sunt derivate din
Throwable,
dar semnificatiile lor sunt diferite, pe cind exceptiile sunt menite sa indice
probleme care pot fi rezolvate pe cale software, erorile indica probleme din
care programele nu se mai pot aduce pe o linie normala de executie, si oprirea
programului este iminenta.
Avantajele,
tratarii exceptiilor este evident, si in special aplicatiile cu profil de
automatizari ar trebui toate sa implementeze aceasta functionalitate cu atit
mai mult cu cit ea poate economisi o sumedenie de resurse financiare prin
prevenirea si preintimpinarea unor eventuale defectiuni sau catastrofe, ne mai
pomenind de eventualele vieti umane salvate.
Astfel
structura ierarhiei de clase Java care caracterizeaza exceptiile este:
Fig
1.2.3.1.1. Ierarhia de clase de exceptii
Pe
cind ierarhia de clase care caracterizeaza erorile sunt:
Fig
1.2.3.1.2. Ierarhia de clase de erori
1.2.4.
Suportul
de retele si programare distribuita
Suportul
de retele implementat de Java se bazeaza pe protocolul TCP/IP (Transmission
Control Protocol/Internet Protocol) dar si UDP/IP( User Datagram
Protoco/Internet Protocol), acelasi protocole ca si cele folosite de Internet
(a se vedea [PH] pentru detalii). TCP/IP-ul implementeaza un set de protocoale
care asigura suportul pentru diferite servicii precum, transfer de fisiere,
loginare la distanta si de asemenea citeva servicii care constituie sistemul de
baza al comunicatiilor.
O
retea conecteaza doua sau mai multe calculatoare in ideea de a desfasura
activitati care de sine independente nu s-ar putea efectua. In afaceri,
educatie, cercetare, retelele au fost mijlocul cel mai comun utilizat pentru
extinderea facilitatilor unui calculator. O explicatie mai simplista ar fi ca
retelele permit mai multor utilizatori sa partajeze si sa foloseasca in comun
mai multe resurse, sau bunaoara sa conlucreze la indeplinirea unei sarcini. La
un nivel mai tehnic o simpla explicatie ar fi, faptul ca un calculator poate sa
partajeze sau sa foloseasca resursele unui alt calculator, un bun exemplu fiind
doua calculatore unul dispunind de facilitati grafice deosebite si celalalt de
calcul de numere reale iesit in comun, daca cele doua sisteme se unesc, atunci
teoretic se pot executa aplicatii care in mod normal ar necesita cel putin un
calculator care sa dispuna de ambele resurse.
1.2.4.1.
Suportul
pentru aplicatii Client-Server
In
ultima decada, aplicatiile client-server au devenit cele mai raspindite tipuri
de aplicatii folosite acolo unde deja exista retele de calculatoare
functionale. Ideea care sta la baza tehnologiei client-server este ca un
calculator specializat are functia de stocare a datelor, precum si de
prelucrare a lor, iar unul sau mai multe calculatoare cu resurse mai mici, au
rolul de a prezenta informatia procesata pe server. Cele mai multe aplicatii
cunoscute si care sunt legate intr-un fel de la o retea de calculatoare au o
arhitectura client-server, si anume ma refer la e-mail, ftp, telnet si asa mai
departe.
Deci
pentru a putea discuta despre o aplicatie client-server, in primul rind avem
nevoie de un server, crearea unui server in limbajul Java este cea mai simpla
metoda pusa la dispozitie posibile. Clasa de baza este denumita
serverSocket
si are functia de a crea un server care asculta la un port specificat si
asteapta clienti care sa se conecteze. Metoda care realizeaza conectarea
efectiva este
accept()din
cadrul clasei aceasta blocind blocul curent al aplicatiei pina cind un client
nu se conecteaza. Datorita modului cum au fost concepute aceste metode in
cadrul limbajului Java, operatiile executate de un client sau de un server
sunt aproape identice. Diferente apar numai in cazul in care serverul are de
tratat mai multi clienti iar clienti doar un singur server.
Motivul
principal al vinzarilor puternice pe care le-a inregistrat limbajul Java,
masina virtuala, este de asemenea motivul principal pentru care dezvoltatori de
aplicatii client-server au investit imens in acest limbaj. Cu portabilitatea
codului greu de atins in alte limbaje Java permite programatorilor sa scrie
codul o singura data independent de platforma, si sa-l distribuie pe masinile
client. Dar in afara independentei de arhitectura Java asigura de asemenea o
puternica biblioteca care permit aplicatiilor sa acceseze in mod rapid
resursele unei retele in maniera adresarii traditionale TCP si localizarii prin
intermediul URL-urilor a resurselor. Un alt avantaj devastator fata de alte
limbaje de programare este incarcarea de la distanta a aplicatiilor. Pe cind in
mod conventional, aplicatiile client sunt nevoite sa fie instalate fizic local
pe fiecare terminal client, limbajul Java permite pe linga incarcarea
aplicatiei direct de pe retea permite si rularea ei pe alt calculator. Astfel
pe linga economiile de resurse, atit umane cit si financiare datorate
eliminarii necesitati instalarii fizice pe fiecare calculator, se obtine
avantajul ca se va lucra tot timpul cu cea mai curenta versiune de aplicatie.
Pachetul
de aplicatii Java apeleaza accesul la suportul de retea prin intermediul unui
set de nivel superior, de functii si streamuri. O aplicatie poate folosi aceste
streamuri pentru a permite datele de intrare si iesire din retea sa poata fi
manipulata intr-o multitudine de moduri. Pachetul
java.io
, care defineste seturile de functii pentru lucrul cu canalele de comunicatie
implementeaza un set de functii deosebit de utile, astfel
DataInputStream,
DataOutputStream
sau
PrintStream
.
Pentru
a demonstra capacitatile si simplitatea cu care se pot asigura functionalitati
deosebite, mai jos se poate observa, un exemplu de realizare a unei conexiuni
la un server.
Socket
conexiune
try{
}
catch (Exception e) {
}
Se
poate observa ca elementele de baza in construirea de programe client-server
sunt socket-urile IP care realizeaza nivelul de comunicatie in cadrul retelei.
In vreme ce programarea socket-urilor poate fi foarte dificila si mare
consumatoare de timp, Java a asigurat o clasa de obiecte proiectate in mod
special pentru a reduce timpul petrecut in scrierea acestor aplicatii. Astfel
clasele
Datagramsocket,
ServerSocket
si
Socket
asigura accesul la protocolul IP, insusi, clasele ca
DataInputStream,
DataOutputstream
si
PrintStream
,
asigura accesul la date, iar in final clasa
StringTokenizer,
asigura functii minime de manipulare a datelor.
1.2.4.2.
Suportul
pentru programarea distribuita
Metoda
prin care se poate realiza programare distribuita in Java se numeste Java
Remote Method Invocation (RMI), si permite scrierea de obiecte distribuite.
Deoarece RMI este centrat in jurul limbajului Java, puterea siguranta si
portabilitatea sunt in mod automat asigurate. Astfel se pot muta comportamente
(executii de obiecte), in acea parte a retelei unde au cel mai mare sens.
La
nivel de baza, RMI este mecanismul Java de a implementa apelul procedurilor de
la distanta (Remote Procedure Call, RPC). RMI insa asigura o serie de avantaje
deasupra RPC, in primul rind din cauza ca RMI face parte integranta din limbaj,
pe cind RPC a fost conceput ca o structura independenta de limbaj, si ca atare
implementeaza numai mecanismele ca
re
sunt comune in cazul sistemelor traditionale. RMI este orientat pe Java, cu
optiuni complete de conectivitate cu alte platforme prin intermediul suportului
pentru metode native pe care Java i-l pune la dispozitie, aceasta insemnind ca
RMI poate aborda o implementare neutra, directa si asigurind performante
maxime, pentru a asigura programatorului o tehnologie de programare distribuita
de inalta performanta.
Avantajele
RMI-ului ar fi:
- Orientat
pe obiecte – RMI poate ceda si primi ca argumente dintr-o metoda/functie
obiecte intregi, si nu numai tipuri de date primitive. In mecanismele RPC
traditionale un astfel de obiect ar fi trebuit descompus, in elemente
primitive, iar de cealalta parte recreat la loc, astfel in Java nu este nevoie
de programare suplimentara pentru a trimite un obiect.
- Comportament
mobil – prin RMI se poate muta comportamentul (implementarea unei clase),
de la client la server sau invers de la server la client, asigurind
flexibilitatea maxima.
- Siguranta
– RMI, foloseste mecanismele de securitate integrate in Java, permitind
asigurarea garantilor pentru momentul in care are loc migrarea componentelor.
Se folosesc intens manageri de securitate care se pot customiza complet pentru
a corespunde cit mai fidel cerintelor.
- Usurinta
in implementare si folosire – RMI transforma scrierea componentelor
server sau client intr-o joaca de copii, fiind suficiente trei lini de cod
pentru a declara un obiect ca fiind server. Simplitatea lui i-l face deosebit
de facil de folosit permitindu-se folosirea lui in fazele de prototip si
testare, aceasta facilitate (de a fi simplu), i-l face nu numai usor de
folosit, dar si usor de intretinut.
- Portabilitatea
garantata – RMI face parte din ideologia Java “Scrie odata,
ruleaza oriunde”, si astfel este garantata rularea codului scris sub
orice masina virtuala.
- Mecanisme
Paralele – Parallel Computing devine posibila, datorita suportului
inglobat in RMI pentru mai multe fire de exectie.
Pentru
a ilustra modul simplu in care functioneaza, se poate urmari diagrama de mai jos:
Fig:
1.2.4.2.1 Exemplu de apel RMI
In
concluzie se poate afirma ca Java asigura un suport real pentru o adevarata
programare orientata pe obiecte si pe deasupra distribuita. Metodele de genul
RMI pot fi folosite pentru a conecta componentele existente cu componente
scrise in alte limbaje de programare, de exemplu. Pentru detalii suplimentare
se poate consulta [WP].
1.2.5.
Suport
pentru securitate avansata
Problemele
de securitate in Java pot fi tratate din diferite aspecte, unul dintre ele sunt
legate inerent de suportul pe care-l asigura pentru retele sau programare
distribuita, aceste au ca punct de pornire securitatea utilizatorului care
ruleaza programe culese de pe Internet, si in special se pune aici problema
applet-urilor. Nu voi insista asupra acestor mecanisme acestea avind putine
interferente cu domeniul automatizarilor. Securitatea din punctul de vedere al
clientului, este rezolvata prin restrictionarea accesului si a posibilitatilor
unui applet de a accesa sistemul local de fisiere, de a fi server de semnal
pentru alte aplicatii, de a accesa alt server, decit cel de pa care a fost
pornit. Bineinteles, este posibila eliminarea acestor restrictii, dar numai
prin interventia explicita si in cunostinta de cauza a utilizatorului.
Celalalt
aspect al securitatii se refera la mecanismele de criptare, codificare si alte
asemenea tehnologii pe care le pune la dispozitie.
Pachetul
de clase de securitate, asigura cinci servicii independente dar totusi
corelate, acestea fiind:
- criptare
de date
- semnaturi
digitale
- canale
de comunicatii securizate
- schimb
de chei
- managementul
cheilor
1.2.5.1.
Criptare
de date
Folosind
algoritmi de criptare cu chei publice, sau simetrice, programele Java pot
folosi pachetul
java.security,
pentru a realiza criptarea sau decriptarea buferelor de date, sau a datelor
folosind cheile specificate. Facilitatile de criptare sau decriptare pot fi
folosite, de asemenea in combinatie cu streamurile standard de intrate sau
iesire de date, astfel fisierele sau socket-urile de comunicatie in retea pot
fi criptate sau decripatate in mod direct si transparent.
1.2.5.2.
Semnaturi
digitale
Semnaturile
digitale sunt utilizate ca o metoda de asigurare ca datele comunicate provin de
unde trebuie, si nu se intimpla un act de frauda. Se pot aplica semnaturi
digitale pe orice tip de document electronic, indiferent daca sunt fisiere
text, date in format binar sau chiar jetoane folosite pentru identificare. In
multe cazuri semnaturile digitale asigura garantii mai puternice decit
semnaturile conventionale, deoarece semnaturile digitale pot demonstra nu numai
identitatea si provenienta unui document, dar si faptul ca documentul respectiv
nu a fost modificat pe parcurs. Pachetul standard Java de securitate
java.security,
asigura toate facilitatile necesare pentru a aplica semnaturi digitale pe
documente, si bineinteles pentru verificarea lor.
1.2.5.3.
Canale
securizate
Un
canal securizat, este un canal de comunicatie care este atit criptat cit si
autentificat. Autentificarea garanteaza ca la celalalt capat nu se afla un
impostor ci sistemul cu care dorim sa comunicam, si criptat pentru a asigura ca
un tert sistem, nedorit nu va putea intelege ce se comunica. Stabilirea unui
canal de comunicatie securizat implica, declinarea identitatii fiecarui
participant, (prin intermediul unor mici mesaje, semnate digital), urmata de o
intelegere intre ambele parti asupra unei chei cu care sa se faca criptarea
datelor transmise prin canalul de date. Dupa ce canalul a fost stabilit,
comunicatia este automat criptata, transmisa, si decriptata la destinatie.
Facilitatile acestea de a stabili canale de comunicare criptate, si sigure sunt
inglobate in cadrul pachetului Java standard
java.security.
1.2.5.4.
Schimburile
de chei
Folosirea
efectiva, a facilitatilor mai sus amintite, presupune ca cele doua, parti
participante, sa schimbe intre ele o cheie de criptare conventionala simetrica,
folosita la criptarea datelor trimise prin canal, si care pe urma este
distrusa, o data cu terminarea legaturi, si deci a canalului de comunicare.
Problema majora in aceste cazuri este ca cheia se interschimba, prin
intermediul unei conexiuni deschise, aceasta nefiind inca criptat. Tehnicile
criptografice au dezvoltat insa mecanisme sigure pentru acest tip de schimb de
chei, prin intermediul unui canal deschis. Aceste mecanisme sunt disponibile
programatorilor prin intermediul pachetului Java standard
java.security.
1.2.5.5.
Managementul
cheilor de criptare
In
cazul precedent, dupa inchiderea unui canal de comunicare, cheile au fost
distruse, odata cu canalul, insa in viata de toate zilele, sun cazuri cind
cheile secrete sau cele publice ale unor grupuri de oameni, trebuie, stocate in
ideea unei folosiri ulterioare. Aceste chei devin inutile, daca nu se stocheaza
si sistemul de unde provin aceste chei, sau nu este asigurat, secretul lor.
Astfel pachetul standard Java, contine mecanisme si metode pentru pastrarea
corespunzatoare a unor astfel de chei, si mentinerea securitati lor.
1.2.6. Suport
pentru baze de date
Bazele
de date au inceput sa capete o importanta deosebita nu numai pentru aplicatiile
generale, ci si pentru cele specializate pe automatizari. Astfel intr-o
aplicatie de control industrial este nevoie sa fie monitorizat sistemul la
fiecare pas, si salvat in acelasi timp, pentru posibile consultari ulterioare,
de asemenea trebuie monitorizate si salvate comenzile care s-au dat pentru a se
putea preciza exact in cazuri de catastrofa, care sunt motivele ei, pentru a
putea fi evitate pe viitor. Pe linga facilitatile mai sus amintite Java mai
asigura suport integrat si pentru baze de date relationale.
Suportul
pentru baze de date este complet pentru toate structurile acestora, fie ca sunt
single-tier, care este structurata ca in figura de mai jos:
Fig:
1.2.6.1 Structura unei aplicatii single-tier
Sau
two-tier, a carei structura se poate observa mai jos:
Fig:1.2.6.2
Structura unei aplicatii two-tier
Setul
de functii JDBC (Java Database Conectivity), a fost proiectat pentru a permite
dezvoltatorilor de aplicatii sa dezvolte cit mai usor cu putinta interfete care
sa lucreze cu baze de date, fara a fi nevoiti sa-si rescrie programele in
permanenta. Pentru a obtine aceste deziderate, firma producatoare Sun impreuna
cu divizia insarcinata cu limbajul Java, Java Soft, a conlucrat impreuna cu
producatori de sisteme de gestiune a bazelor de date, pentru a reusi sa
sintetizeze un set de librarii pentru lucrul cu bazele de date, care sa nu tina
seama de tipul de baze de date cu care se lucreaza, creindu-se astfel o
interfata comuna, pentru aplicati, indiferent de tipul de baze de date pentru
care sunt scrise aplicatiile. Interogarile bazelor de date putindu-se face in
limbajul standard SQL.
1.2.7. Personal
Java
Personal
Java este un nou mediu de dezvoltare a aplicatiilor Java pentru dispozitive
conetabile in retea pentru dispozitive cu capacitati mai reduse decit
calculatoarele personale, bunaoara dispozitive de uz casnic sau industrial.
Deoarece PersonalJava include o numai o numita parte din functiile Java
standard, aplicatiile scrise pentru PersonalJava sunt perfect compatibile in sus.
Motivele
pentru care Java are viitor in acest domeniu sunt, mecanismele de incarcare si
executie dinamica a codului, putindu-se in acest fel, sa se faca modificari
foarte usor in codul aplicatiilor, totodata, dimensiunea redusa a codului
generat, in special datorita faptului ca codul binar Java este mult mai
compact, decit majoritatea codurilor, si in cele din urma, este printre primele
limbaje care aduce tehnologia orientata pe obiecte in astfel de dispozitive.
Java i-si mentine portabilitatea si in acest domeniu, tocmai datorita faptului
ca el ruleaza in interiorul unei masini virtuale. Practic se poate scrie in
sfirsit codul pentru implementarea unui regulator pentru un proces pentru un
sistem bazat pe 8051, si fara modificari el va rula si pe un microcontroler,
din familia PIC-urilor.
PersonalJava
include o masina Java virtuala, un set de librarii de baza, si un set de
librarii optionale, care pot fi folosite dupa preferinta. Pachetele sunt
concepute modular si scalabile, permitind, de exemplu folosirea unor
dispozitive diferite de afisare, sau protocoale diferite de retea.
1.2.8. Embedded
Java
EmbeddedJava
este in mod asemanator cu PersonalJava, o versiune de Java pentru dispozitive
cu facilitati restrinse, cu deosebirea ca este mult mai putin pretentios decit
personal Java, astfel a fost proiectat sa fie mult mai modular, scalabil si
configurabil, rulind cu un minim posibil de memorie.
EmbeddedJava
asigura o serie de nivele de functionalitate, bunaoara daca se doreste
implementarea parti de programe pentru un dispozitiv in clasa pagerelor,
deoarece acestea nu au nevoie, de exemplu de un sistem de fisiere, acesta poate
fi exclus din pachetele, care vor fi implementate in microcontroler. Aceasta
versiune, asigura suportul unor instrumente absolut necesare, cum ar fi, unelte
de inserare a codului executabil in memoriile ROM, de asemenea pentru
compilarea, compresia si plasarea imaginilor, si pentru estimarea resurselor de
calcul necesare.
1.3. Controlul
si monitorizarea proceselor prin intermediul retelelor, locale sau cu arie
larga de raspindire.
Structura
schematica a unei structuri de control a unui proces prin intermediul,
retelelor de calculatoare si in speta prin intermediul Internetului, poate
fi descrisa prin figura urmatoare:
Fig:
1.3.1 Structura de distribuire a unui proces, prin WWW
Elementele
care apar in figura 1.3.1 sunt:
- Procesul
– reprezinta elementul care urmeaza a fi distribuit, din acest punct de
vedere, el nu are nici un element deosebit fata de o schema de control ne
distribuita.
- SCADA
–
acronimul
provinede la
Supervisory
Control and Data Acquisition, ele este elementul principal, responsabil de
monitorizarea si conducerea procesului, pe linga functia sa principala, cea de
a monitoriza procesul el, este deasemenea responsabil cu distribuirea
semnalelor provenite de la proces. Toate semnalele de control si monitorizare
externe pornesc de la el si ajung la el. Este punctul cel mai sensibil, si
totodata elementul care trebuie sa suporte modificari majore, pentru a putea
realiza o structura de monitorizare si control distribuit.
- Server
de WWW –
acesta poate fi privit ca punctul central de distributie a
semnalului, el are sarcina de a asigura suportul pentru monitorizarea
procesului din exterior. Prezenta lui nu este mandatorie, dar existenta lui
asigura o serie de mecanisme care pot facilita in mod deosebit distributia
procesului, sau mai degraba a distributia programelor care reprezinta clientul
serverului de semnal, imaginea in oglinda a masini SCADA.
- Clienti
–
Reprezinta sistemele care monitorizeaza sau conduc procesul de la distanta.
- Intranet
–
Este
o structura de retele care asigura suportul de distributie a informatiei,
intr-o comunitate inchisa. Procesul nu trebuie sa fie distribuit, neaparat unei
lumi intregi, el putind fi distribuit doar in interiorul firmei, existind
avantajul net, a existentei mai multor clienti, care pot monitoriza in acelasi
timp procesul, fara a fi nevoie ca sa se gaseasca la locul procesului.
- Internet
–
Reprezinta structura de distributie cunoscuta, care ofera avantajul major, al
omniprezentei si al pretului de cost redus, tocmai din cauza ca, deja exista
aproape in toate institutiile.
1.3.1. Structura
de distributie a procesului
Principalele
motive, sau avantaje pe care le prezinta conducerea si monitorizarea proceselor
de la distanta pot fi prezentate sub doua aspecte:
- Distributia
unui proces, in ideea de a fi monitorizat de oriunde (de a fi accesibil de
oriunde), permitindu-se astfel existenta a mai multi clienti care urmaresc
procesul in acelasi timp. Aceasta structura poate fi urmarita si in figura 1.3.1.
- Distributia
unui proces, in ideea de a se permite o monitorizare centralizata. Acesta este
cazul in care, procesul este format dintr-o multitudine de subprocese, situate
la distante mari, intre ele (dispersate), si la cere controlul per ansamblul
proceselor era greu de realizat. O posibila structura a acestui model poate di
observata in figura de mai jos.
Fig:1.3.1.1
Monitorizarea centralizata a mai multor procese
Din punct de vedere functional, controlul de la distanta a unui proces se
conformeaza, cu modelul tipic de aplicatie client-server. Astfel aplicatia se
poate, separa in doua module.
Fig:
1.3.1.2 Structura client-server
Serverul
de date poate coincide cu sistemul responsabil de achizitionarea semnalului de
la proces si cu controlul acestuia, dar aceasta numai in cazul, in care
procesul nu este puternic distribuit, altfel performantele si functionalitatea
acestuia ca controlor de proces poate fi mult afectata, cea ce ar putea
pereclita siguranta procesului. In cazul in care totusi, procesul este puternic
distribuit, se poate folosi un mecanism de multiplexare a semnalului.
Fig:
1.3.1.3 Structura client-server cu multiplexor de semnal
Multiplexorul
de semnal are sarcina de a degreva, controlerul de proces de sarcina de a
deservi clienti multipli.
1.3.2. Serverul
de semnal
Semnalele
provenite de la proces, sunt achizitionate sau centralizate de catre
calculatorul central al procesului, aceste semnale in cazul exportarii
procesului trebuie facute “publice”. Deoarece aceste semnale sunt
disponibile in totalitatea lor doar in calculatorul central, el este cel care
are sarcina de a le duplica, si exporta, fiind in acelasi timp responsabil de
ascultarea comenzilor provenite de la distanta, si de interpretarea lor.
Deoarece
intr-un proces sunt de obicei disponibile mai multe semnale, provenite de la
diverse dispozitive, (senzori, traductoare), acestea trebuie citite si
prelucrate in mod continuu, deci in mod automat avem de a face cu o aplicatie
multitasking, indiferent de modul cum a fost implementat acest multitasking.
Astfel exista mai multe posibilitati de a distribui semnalul:
- individualizat
pe dispozitiv
- global
centralizat.
1.
Individualizat pe dispozitiv, permite o mai buna structurare a controlului, o
astfel de aplicatie avind structuri bine formate care sa reprezinte distinct
dispozitivele pe care le conduce. Teoretic in acest caz, aplicatia de control
se compune dintr-o multitudine de astfel de structuri functionale care sunt
suficient de dezvoltate pentru a sti sa prelucreze si coordoneze singura
dispozitivul pe care-l coordoneaza. In acest caz, elementul de servire a
datelor se integreaza in aceasta structura.
Fig:1.3.2.1
Structura individualizata pe dispozitive
Pentru
a fi capabila de a exporta semnalul, trebuie modificate doua blocuri
functionale si anume, partea de citire a semnalului si partea de elaborarea a
comenzi. Structura modificata a acestora poate fi urmarita mai jos.
Fig:1.3.2.2
Structura citeste semnal, modificata pentru exportul datelor
Fig:1.3.2.3
Structura elaboreaza comanda, extinsa pentru distribuirea procesului
- Global
centralizat – presupune operatiile de colectare si distribuire a
semnalelor centralizate intr-un singur loc, de unde se apeleaza pe rind sub
forma de proceduri sau functii, elementele de control, afisare si prelucrare a
semnalelor. In acest caz exportarea semnalelor, se face in cadrul blocului
centralizator
Fiecare
metoda prezinta avantaje si dezavantaje, mai mult sau mai putin obiective,
legate de claritatea implementari, de performante si de facilitatile pe care le
asigura unei dezvoltari ulterioare.
1.3.3. Clientul
de semnal
Desi
indeplinesc functii diferite, la baza ele nu difera foarte mult ca
implementare, deoarece la fel ca si serverul de proces si clientul are nevoie
de o sursa de semnal, genereaza o comanda, si efectueaza functii de prelucrare
si afisare asupra semnalului. Se poate spune ca clientul este un server, fara
facilitatile de distributie a semnalului. Aceasta remarca, nu-si are baza in
teoria mecanismelor client-server, ci doar cind este vorba de servere si
clienti de procese, deoarece si un server de proces, este la rindul lui un
client, in toata puterea cuvintului al procesului.
Deci,
asupra clientul de proces, se poate aplica aceiasi structura ca si a
serverului. Daca se pastreaza si mecanismele de server, atunci acesta poate fi
la rindul sau server de proces, permitindu-se realizare unor lanturi de semnal.
1.3.4. Structura
de comunicatie
Din
punctul de vedere al protocoalelor folosite pentru transmiterea datelor, se pot
mentiona un numar de doua, mai importante:
- TCP
- UDP
Fiecare
din aceste protocoale au avantajul si dezavantajul lor, sau mai degraba
functionalitatea lor.
- TCP
– este un protocol, foarte raspindit, si foarte utilizat, principalul
punct forte este ca pe parcursul transmisie, pachetele de date transmise ajung
in aceiasi ordine si fara pierderi de date.
- UDP
– este un protocol, destinat transmisiilor, la care pierderea unor
pachete de date nu este o catastrofa, ba chiar recuperarea lor ar fi. Se
foloseste in special acolo, unde datele imbatrinesc foarte repede, si dina
acest motiv, o eventuala recuperare a unui pachet pierdut, nu prezinta nici o
importanta, deoarece data este oricum veche, si deci invalida.
Pentru
mai multe detalii asupra protocoalelor se poate consulta [VALEAN97].
Din
punct de vedere al incapsulari datelor acestea pot fi, de tip canal si
dispozitiv, sau de tip singur canal.
Canal
si dispozitiv presupunea asignarea unui canal individual, fiecarui, semnal
individual din proces.
Un
singur canal presupune, comprimarea si codificarea datelor tuturor
dispozitivelor pe un singur canal de comunicatie, singurul dezavantaj, insa al
acestei tehnici, este pierderile de timp, datorita, codificari –
decodificari semnalelor la cele doua capete ale transmisie.
1.3.5. Instrumente
virtuale
Folosirea calculatoarelor pe post de elemente indicatoare, si de interactiune
cu clientul, a dus la aparitia unui nou tip de instrumente, denumite
instrumente virtuale, instrumente deoarece asigura toate functionalitatea unui
instrument real, si virtuale deoarece exista numai sub forma unui program rulat
pe un calculator. Facilitatile unor astfel de instrumente sunt imense, in
primul rind custurile de executie sunt reduse, ele fiind doar programe, pe de
alta parte nu se defecteaza niciodata, si ofera o interfata, sugestiva pentru
utilizator. Un astfel de instrument inglobeaza toate functiile unui instrument
real, citeste semnalul, il prelucreaza, si afiseaza, preia comanda de la
utilizator, o prelucreaza, si prin intermediul diverselor drivere, o trimite
dispozitivului.
Descrierea
pachetului JaRPE
Acronimul de JaRPE, provine de la JavA Remote Process Explorer si cuprinde un
set de programe si librarii pentru demonstrarea facilitatilor limbajului Java,
in construirea diverselor aplicatii si in speta pentru controlul de la distanta
a proceselor industriale, precum si pentru realizarea unor instrumente virtuale.
Deci
componenta pachetului se poate descrie in felul urmator:
- JaRPE
- Javins
-
contine setul de baza de instrumente virtuale, din care, conform modelului
programarii pe obiecte se poate mosteni 80% din comportamentul unui instrument
virtual, raminind de implementat numai interfata acestuia, asa cum este ea
dorita si anumite aspecte ale comunicatiei. In acest pachet sunt puse la
dispozitie pe linga modelul de baza, inca trei componente: un buton, un buton
multipozitional cu maxim patru pozitii si un afisaj gen ecran osciloscop.
- JaRPEdit
-
contine programele unui editor de interfete pentru procese, de altfel de acolo
i-si trage numele
JavA
Remote Process Editor
.
Este un mediu vizual, orientat pe componente, cu ajutorul caruia se pot elabora
foarte facil interfete inteligente de proces, componentele acestui editor se
pot extinde sau adauga cu usurinta, fiind la baza componente Java standard
(Java Beans) si prezentind functionalitatea unor instrumente virtuale.
- SmartApplet
–
reprezinta componenta care se poate rula atit ca aplicatie de sine
independenta, cit si ca applet, intr-o pagina HTML si a carei rol functional
este de a permite crearea unui interfete, stabilita in editorul vizual de
interfete si pasata prin intermediul unui fisier cu extensia
itf,
salvat de asemenea de catre editorul de interfete.
2.1. Instrumente
Virtuale Java (Javins)
Instrumentele Virtuale Java asigura toata functionalitatea unor instrumente
virtuale, permitind receptionarea, prelucrarea, distribuirea semnalului, precum
si preluarea comenzi de la utilizator sau de la un instrument virtual aflat la
distanta si trimiterea ei la sursa semnalului.
Un
Javins i-si poate prelua semnalul, fie dintr-o sursa locala de semnal, sursa
locala reprezentata printr-o clasa sau un obiect al clasei respective care
realizeaza operatia de citire a dispozitivului fizic si elaborarea unor
streamuri de date (intrare si iesire) pe care Javins-ul sa poata sa le
foloseasca. O alta optiune este sa preia aceleasi streamuri de date, dintr-o
sursa de semnal situata la distanta, sursa de semnal fiind specificata
printr-o adresa URL si un port de ascultare. Aceasta delimitare este insa
doar, in interiorul metodei de initializare a conexiuni, dupa aceasta,
Javins-ul trateaza in mod unitar streamurile de date, indiferent de provenienta
lor.
Una
din metodele fundamentale ale programari orientate pe obiecte, afirma separarea
portiuni de cod care poate suferi modificari de partea de cod care este tot
timpul stabila. Din acest motiv s-a optat pentru utilizarea unei alte clase
pentru realizarea unei transfer de semnal, dintr-o sursa de semnal interna.
Din
punctul de vedere al interfetei, un instrument virtual prezinta trei zone
distincte, asa cum se poate urmari si in figura de mai jos:
Fig:2.1.1
Interfata unui Javins
Algoritmul
de functionare, simplificat a unui Javins poate fi urmarit in diagrama de mai
jos:
Fig:
2.1.2 Algoritmul de functionare simplificat al unui Javins
2.1.1. Comunicatia
Comunicatia
prezinta aspectul cel mai important al instrumentelor virtuale, ele trebuie sa
preia un semnal si de asemenea trebuie sa genereze o comanda.
Intern,
sursele de semnal folosite sunt implementate sub forma unei clase denumite
SignalPair
, aceasta este o clasa fara metode si implementarea ei poate fi urmarita mai jos
:
public
class SignalPair {
/**
* signalIn-este streamul care asigura datele de intrare
*/
public DataInputStream signalIn;
/**
* signalIn - este streamul care asigura iesirea datelor
*/
public DataOutputStream signalOut;
}
DataInputStream
si
DataOutputStream
reprezinta
streamuri de intrare si iesire Java care ofera posibilitatea formatari datelor.
SignalIn
si
SignalOut
reprezinta streamurile de intrare si de iesire de date, aceste streamuri sunt
culese direct de la sursa, dar intern, in mod direct se lucreaza numai cu
streamul de iesire al datelor
SignalOut
cel de intrare fiind folosit pentru a deriva un alt stream,
SignalInputStream,
care are particularitatea ca datele care sunt citite din el sunt modificate de
catre un obiect modificator, permitindu-se in acest fel efectuarea unor
operatii de prelucrare asupra datelor de intrare .
Se
poate observa ca datele de la sursa receptionate de catre streamul de intrare
SignalIn sunt prelucrate de catre modificatorul de semnal si apoi scrise in
format SignalInputStream si folosite pentru afisare, in acest fel permitindu-se
efectuarea diferitelor operatii simple sau complexe asupra lor.
Algoritmul
de extragere a streamurilor de date, implementat de metoda
initializeConection,
poate fi urmarit mai jos:
Pentru
a putea permite o implementare facila a acestor mecanisme de preluare a sursei
de semnal, s-au implementat doua tipuri de interfete si anume L
ocalSignalSource
si
RemoteSignalSource.
Astfel
se asigura uniformitatea implementarii lor. Definitiile acestor interfete sunt:
public
interface LocalSignalSource {
/**
* Pastreaza numele clasei din care se instantiaza o sursa locala
*/
String localSignalSourceClass = "";
LocalSignalSource localSignalSource = null;
/**
* Asigura metodele standard pentru un Bean de setare si preluare
* a numelui clasei din care se va instantia obiectul
* @param localSignalSourceClass - numele clasei din care se va
* instantia obiectul
*/
public void setLocalSignalSourceClass(String localSignalSourceClass);
/**
* Asigura metodele standard pentru un Bean de setare si preluare
* a numelui clasei din care se va instantia obiectul
* @return localSignalSourceClass - numele clasei din care se va
* instantia obiectul
*/
public String getLocalSignalSourceClass();
public void setLocalSignalSource (LocalSignalSource localSignalSource);
public LocalSignalSource getLocalSignalSource ();
/**
* Realizeaza instantierea obiectului.
* Din numele clasei setat, se creaza clasa si se instantiaza
* @return LocalSignalSource - instanta generatorului de semnal local
*/
public LocalSignalSource getLocalSignalSourceInstance();
/**
* Din instanta de generator de semnal local, data initial ca si nume de
* clasa, se culege perechea de streamuri de data (DataInputStream,
* DataOutputStream), pentru comunicatia bidirectionala
* @return SignalPair - Cele doua streamuri (In/Out de date)
*/
public SignalPair getLocalSignalPair();
}
Definitia
interfetei de conectare de la distanta este:
public
interface RemoteSignalSource {
String sourceURL ="" ;
int sourcePort=3000;
/** Seteaza URL-ul la care se va conecta in vedera
* comunicatie, cu obiectul oglinda, generator de semnal
* @param sourceURL - URL-ul
*/
public void setSourceURL(String sourceURL);
/**
Returneaza URL-ul la care se va conecta in vedera
*
comunicatie, cu obiectul oglinda, generator de semnal
* @return - URL-ul
*/
public String getSourceURL();
/** Seteaza portul la care se va conecta in vedera
* comunicatie, cu obiectul oglinda, generator de semnal
* @param sourcePort - portul
*/
public void setSourcePort(int sourcePort);
/** Returneaza portul la care se va conecta in vedera
* comunicatie, cu obiectul oglinda, generator de semnal
* @return - portul
*/
public int getSourcePort();
/** Incearca conectarea la sursa de semnal, situata
* remote, prin intreemediul internetului sau a
*intranetului
*/
public void connectRemoteSignalSource();
/**
* Se culege perechea de streamuri de data DataInputStream,
* DataOutputStream), pentru comunicatia bidirectionala
*/
public SignalPair getRemoteSignalPair();
}
Cele
mai importante metode din aceste interfete sunt :
connectRemoteSignalSource
pentru
conectarea la distanta si
getLocalSignalSourceInstance
pentru
o sursa de semnal locala, amindoua creind in acest fel legatura cu sursa de
semnal. Aceste metode pot ramine nemodificate, pe parcursul creari altor
Javins-uri, ele mentinindu-se .
Implementarea
reala a celor doua metode se poate observa mai jos:
public
void connectRemoteSignalSource() {
if ( sourceURL != null
&& sourceURL != ""
&& sourcePort != -1 ) {
try {
remoteSocket = new Socket (
InetAddress.getByName(sourceURL)
, sourcePort);
} catch (Exception ex) {
System.err.println("Problem Conection to host : "
+ sourceURL + " on port : "
+ sourcePort + " failed !");
ex.printStackTrace();
alertLabel.setMessage(true, "R.C. Failure");
System.err.println(“remoteSocket initialized to –“
+ “NULL -- !");
}
}
else remoteSocket = null;
}
Se
poate observa crearea unei conexiuni, bazata pe protocolul TCP/IP, prin
intermediul constructorului
Socket(InetAddres,
port)
,
de asemenea se poate observa, mecanismul de tratare a erorilor.
Pentru
conectarea la sursa locala de semnal mecanismele, sunt mai complicate, deoarece
singurul lucru pe care il avem este numele clasei care a fost implementata
pentru a face semnalul fizic disponibil componentei software, fara a fi nevoiti
sa o modificam, pentru a putea sa preia semnalul:
public
LocalSignalSource getLocalSignalSourceInstance () {
String className = getLocalSignalSourceClass();
if (className != "" &&
className != null &&
localSignalSource == null) {
try {
Class localSignalSClass = Class.forName(className);
LocalSignalSourcelss =(LocalSignalSource)
localSignalSClass.newInstance();
setLocalSignalSource(lss);
return lss;
} catch (ClassNotFoundException ex) {
System.err.println("Problems in :" +
this.getClass().getName() +
" at getLocalSignalSource -> " +
className + " not found !");
ex.printStackTrace();
System.err.println("LocalSignalSourceinitializet to”
+ “ -> NULL <- !");
return null;
}
catch (Exception ex) {
System.err.println("Problems in :" +
this.getClass().getName() +
" at getLocalSignalSource -> " +
className + " can not be “
+
“initialized !");
ex.printStackTrace();
System.err.println("LocalSignalSource initialized “
+
“to -> NULL <- !");
return null;
}
}
else return null;
}
Se
poate observa, cum dintr-un sir de caractere care reprezinta numele unei clase
se creeaza un obiect nou. Aceasta presupune doua etape:
- Crearea
clasei
- Instantierea
clasei
Crearea
clasei se face prin intermediul metodei
class.forName(String
param)
,
dupa ce s-a creat (mult mai corect ar fi spus incarcat) clasa, se poate
instantia obiectul prin intermediul metodei
newInstance(),
astfel pornind de la un sir de caractere, in final avem un obiect valid capabil
sa capteze un semnal de la un dispozitiv fizic, prin intermediul driverelor sau
al accesului direct prin porturi sau intreruperi.
2.1.2. Serverul
de semnal
Orice instrument virtual, indiferent daca este la rindul ei client, de la
distanta, poate fi server de semnal, facilitind astfel crearea lanturilor de
instrumente virtuale distribuite. Un Javins poate deveni server de semnal prin
setarea portului pe care se va instaura , noul serviciu si setarea unei
variabile logice prin intermediul metode,
setServer([valoare
logica]),
aceasta fiind o metoda pe care fiecare Javins o mosteneste si deci o are
automat implementata.
In
caz, ca se doreste ca un Javins sa fie server de semnal, atuncea acesta
porneste un fir de executie separat, pentru a nu perturba functionalitatea de
baza a componentei. In acest moment acest server de semnal este un server
secvential , si nu unul multithreading. Implementarea unui server de semnal
care sa poata suporta mai multi clienti in acelasi timp ar duce la scaderi
nedorite ale performantei. Dar un asemenea mecanism, nu este dificil de
realizat.
Clasa
care implementeaza serverul de semnal se numeste
JavinsDspecher
si
primeste ca parametru pe insusi Javins-ul care la creat. Aceasta este necesar
pentru a putea folosi la trimiterea si receptionarea datelor, metodele corecte,
dedicate acestui scop din cadrul Javins-ului.
Secventa
de cod care porneste serverul este:
public
void startServer() {
if (isServer()) {
javinsDispecher = new JavinsDispecher (this);
serverThread = new Thread (javinsDispecher);
System.out.println("Prepair to start server ");
serverThread.start();
}
}
Se
poate urmari astfel usurinta cu care se poate porni un nou fir de executie.
Apelarea
metodei
start()
de fapt porneste firul de executie, metoda care va fi rulata, va fi
run().
Se
poate urmari mai jos bucla principala care accepta o conexiune, si o deserveste:
public
void run() {
while(true) {
System.out.println("Waiting for clients");
try {
socket = serverSocket.accept();
System.out.println("I have clients");
baseJavins.communicate("New Client:" +
socket.getInetAddress().getHostAddress());
rDataIn = new
DataInputStream(socket.getInputStream());
rDataOut= new
DataOutputStream(socket.getOutputStream());
for ( ; ;) {
(Thread.currentThread()).yield();
baseJavins.writeSignal2Server(rDataOut);
baseJavins.readSignalFServer(rDataIn);
}
}catch (SocketException ex) {
System.out.println("Client lost !");
baseJavins.communicate("Client Lost")
}
catch (Exception ex) {
System.out.println("Client lost !");
}
}
}
La
crearea unui nou tip de componenta, este suficient sa se rescrie metodele
writeSignal2Server
si
readSignaFServer
si astfel metodele supradefinite vor fi apelate in acest ciclu.
2.1.3. JaviButton
Acesta este o componenta standard, reprezentind functional un buton cu doua
stari, care pot fi codificate prin ON si OFF.
Imaginea
unui astfel de Javins, poate fi urmarita in figura de mai jos:
Fig:
2.1.3.1 Instanta de JaviButton
Implementarea
care difera de Javins-ul de baza sunt cele legate de interfata, precum si
metodele de scriere semnal, citire semnal, scriere semnal la server, citire
semnal de la server.
Implementarea
metodei de citire semnal este:
public
void readSignal(SignalInputStream mySignalIn) throws
IOException{
byte readValue = mySignalIn.readByte();
if ( readValue == 2 ) {
if (!value) {
b.setForeground(Color.red);
b.setLabel("ON");
synchronized(this) {
value = true;
}
ActionEvent ne = new ActionEvent(this, 1, "ON");
processActionEvent(ne);
}
}
else if (readValue == 1) {
if (value) {
b.setLabel("OFF");
b.setForeground(Color.green);
synchronized(this) {
value = false;
}
ActionEvent ne = new ActionEvent(this, 1, "OFF");
processActionEvent(ne);
}
}
}
Semnal
trimis este totdeauna valoarea semnalului incrementata cu unu, pentru a putea
face distinctie, la receptionare de valorile de confirmare receptie care este
tot timpul zero.
De
asemenea se pot observa, sectiunile de sincronizare, formatate in cadrul
blocurilor
synchronized(this),
aceasta metoda protejind de alterarea valorilor, in cadrul contextelor de
executie multifir.
Implementarea
metodei de scriere semnal este:
public
boolean writeSignal(DataOutputStream myDataOut) throws IOException{
if (getLocalSignalSource() == null) {
if (newValue) {
int byteValue = pip.read();
myDataOut.writeByte((byte)byteValue);
synchronized(this) {
newValue = false;
}
return true;
} else {
myDataOut.writeByte(0);
return true;
}
}
else if (newValue) {
myDataOut.writeByte(valueByte);
synchronized(this) {
newValue = false;
}
}
else myDataOut.writeByte(0);
return true;
}
Se
poate observa, ca daca nu este, valoare noua a comenzi se trimite, valoare de
confirmare a receptiei zero. Algoritmul de transmisie se poate spune ca face
parte din clasa simplex, de tip send and wait, mai multe amanunte despre
aceasta se poate studia in [VALEAN97]. Nu este cea mai eficace metoda de
transmisie receptie, dar scopul proiectului este didactic.
Variabila
newValue
este folosita cu rol de semafor, semnalizind de fiecare data cind exista o noua
valoare a semnalului de comanda, valoare care se transmite, in loc de semnalul
de confirmare.
2.1.4. JaviMultiPos
Acesta este o componenta standard, reprezentind functional un buton cu mai
multe stari (maxim posibile patru), care pot fi codificate prin intermediul
unui sir de caractere, in are valorile individuale sunt separate prin virgula.
Imaginea
unui astfel de Javins, poate fi urmarita in figura de mai jos:
Fig:
2.1.4.1 Instanta de JaviMultiPos, cu patru stari
Implementarea
care difera de Javins-ul de baza sunt cele legate de interfata, precum si
metodele de scriere semnal, citire semnal, scriere semnal la server, citire
semnal de la server.
Implementarea
metodei de citire semnal este:
public
void readSignal(SignalInputStream mySignalIn) throws IOException{
byte readValue = mySignalIn.readByte();
if ( readValue != 0 ) {
b.setCurrent(readValue -1);
ActionEvent ne = new ActionEvent(this, 1, readValue-1 + "");
processActionEvent(ne);
}
}
Se
poate observa, ca daca valoarea citita, este diferita de zero, acesta fiind
semnal de confirmare a transmisiei, se seteaza valoare butonului
multipozitional. Acest buton multipozitional, este o componenta scrisa special,
care permite sa se discretizeze, anumite valori si sa se afiseze valoare
cuantei curente.
Structura,
de scriere a semnalului, este implementata ca si mai jos:
public
boolean writeSignal(DataOutputStream myDataOut) throws IOException{
if (getLocalSignalSource() == null) {
if (newValue) {
int byteValue = pip.read();
myDataOut.writeByte((byte)byteValue);
synchronized(this) {
newValue = false;
}
return true;
} else {
myDataOut.writeByte(0);
return true;
}
}
else if (newValue) {
myDataOut.writeByte(valueByte);
synchronized(this) {
newValue = false;
}
}
else myDataOut.writeByte(0);
return true;
}
Se
poate observa ca mecanismul de scriere este aproximativ identic cu cel al
componentei
JaviButton,
folosindu-se acelasi semafor
newValue,
pentru semnalizarea unei valori noi.
Metodele
speciale de scriere a semnalului catre server, este:
public
boolean writeSignal2Server(DataOutputStream signalOut) throws
IOException{
signalOut.writeByte(b.getCurrent()+1);
return true;
}
Iar
cel de citire de la serverul de semnal, pe care il implementeaza, daca il
implementeaza, se poate observa mai jos:
public
void readSignalFServer(DataInputStream signalIn) throws IOException{
int readValue;
readValue = signalIn.readByte();
if (readValue != 0) {
System.out.println(readValue);
pop.write(readValue);
valueByte = (byte)readValue;
pop.flush();
newValue = true;
communicate("new remote value");
}
}
Se
poate observa, aceeasi delimitare, dintre semnal zero receptionat si alta
valoare.
2.1.5. JaviDiagram
Acesta este o componenta standard, reprezentind functional un afisaj, stil
osciloscop, pe care fiecare valoare a semnalului este afisat cu o diviziune mai
mare pe scara timpului, axa X.
Imaginea
unui astfel de Javins, poate fi urmarita in figura de mai jos:
Fig:
2.1.5.1 Instanta de JaviDiagram
Implementarea
care difera de Javins-ul de baza sunt cele legate de interfata, precum si
metodele de scriere semnal, citire semnal, scriere semnal la server, citire
semnal de la server, totusi diferentele sunt ca acest Javins spre diferenta de
cele prezentate anterior, nu poate trimite sau accepta comenzi, el putind doar
citi date.
Implementarea
metodei de citire semnal este:
public
void readSignal(SignalInputStream mySignalIn) throws IOException{
byte readValue = mySignalIn.readByte();
if ( readValue != 0 ) {
b.draw(readValue -1);
valueByte = readValue;
synchronized (this) {
newValue = true;
}
}
}
Diferentele,
fata de implementarile anterioare, sunt minore.
Implementarea,
metodei de scriere semnal, la serverul de semnal este :
public
boolean writeSignal2Server(DataOutputStream signalOut) throws IOException{
if (newValue) {
signalOut.writeByte(valueByte);
synchronized(this) {
newValue = false;
}
}
else signalOut.writeByte(0);
return true;
}
Metoda
de scriere fiind aproape identica, cu cea de la alte componente.
2.2. Editorul
vizual de interfete (JaRPEdit)
Pentru
a demonstra facilitatile, programari orientate pe obiecte, precum si a
programari pe componente, sa construit un editor de interfete, care sa
exploateze facilitatile oferite de Javins-uri.
Editorul
este compus din trei parti:
- Fereastra
principala
- Fereastra
cu unelete
- Fereastra
de lucru
Cele
trei ferestre pot fi urmarite in figurile de mai jos:
Fig:
2.2.1 Fereastra principala
Fig:
2.2.2 Fereastra cu unelte
Fig:
2.2.2 Fereastra de lucru
Principalele
functionalitati ale editorului de interfete sunt:
- Incarcarea
de componente
- Plasarea
de componente
- Modificarea
proprietatilor componentelor
- Salvarea
componentelor si a proprietatilor lor intr-un format care sa faciliteze
incarcarea si executia lor
2.2.1. Incarcarea
componentelor
La
pornire programul cauta, directorul Jars, din cadrul directorului de start,
acest director contine fisiere, cu extensia jar. Java ofera o metoda standard
de a livra pachetele de programe, acestea sunt arhivate sub format ZIP sau
GZIP, si plasate in cadrul unui fisier cu extensia jar. Avantajul pe linga,
reducerea spatiului de stocare, este ca fisierul contine un declarator, care
este un fisier inglobat, care contine numele tuturor claselor, din cadrul
pachetului, precum si daca clasa respectiva reprezinta o componenta sau nu.
Deci
programul scaneaza, toate fisierele cu extensia jar din directorul respectiv,
si acolo unde gaseste componenta, o incarca ti mai apoi o instantiaza.
Mecanismul de instantiere este descris, la capitolul de Javins-uri, la
descrierea metodei l
ocalSignalSourceInstance.
2.2.2. Plasarea
componentelor
Aceste
componente, in momentul in care se lucreaza cu ele, sunt active, deci ruleaza,
pentru a face publice proprietatile componentelor, si aceasta doar, in cadrul
editorului vizual de interfete si nu in cadrul aplicatiilor, unde un astfel de
comportament nu este de dorit, sa folosit o clasa care sa invaluie componenta
si la anumite operatii ale utilizatorului s-i dezvalui proprietatile pentru
modificare. Aceasta clasa, se numeste
Wrapper,
si rolul ei poate fi oarecum vizualizat in figura urmatoare:
Fig:
2.2.2.1 Functionalitatea wrapperului
Procedura
de adaugare a unei componente in cadrul unui wrapper poate fi vazuta mai jos:
public
Wrapper(Object bean, String beanName, String beanLabel) {
System.out.println(beanName + " : " + beanLabel);
this.bean = bean;
this.beanLabel = beanLabel;
if
(beanName == null) {
beanName = bean.getClass().getName();
}
this.beanName
= beanName;
setLayout(null);
if
(Beans.isInstanceOf(bean, Component.class)) {
child = (Component)Beans.getInstanceOf(bean, Component.class);
}
popmen=
new WrapperPopupMenu(beanName, beanLabel);
this.add(child);
this.add(popmen);
this.addMouseListener(this);
this.addMouseMotionListener(this);
child.setLocation(borderWidth,
borderWidth);
initialize();
doLayout();
}
Acest
wrapper dupa ce a inglobat componenta se poate plasa pe suprafata activa de
lucru ca o componenta normata.
2.2.3. Modificarea
proprietatilor componentelor
Motivul existentei unui asemenea mediu de dezvoltare vizual, nu este
justificat, daca nu se lucreaza, cu componente care pot fi modificate, intr-un
mod intuitiv si vizual. Java asigura o tehnica foarte performanta, care se
numeste reflectie si care permite determinarea exacta a metodelor, a
variabilelor de instanta si a constructorilor unui obiect. Astfel unui obiect,
activ, care ruleaza i se poate determina in mod activ, metodele pe care le pune
la dispozitie, sau cu alte cuvinte un obiect permite sa i se cunoasca ce
obiecte ii determina starea si ce comportamente pune la dispozitie pentru a i
se modifica starea. In cazul componentelor standard Java, descrise ca Java
Beans, se merge mai departe, existind o clasa implementata in cadrul pachetului
Java standard denumita
PropertiesDescriptor
care identifica, conform unei conventii exact care proprietate a componentei se
doreste a fi publica, si prin intermediul caror metode se scrie si se citeste
acea proprietate.
Implementarea
acestei functionalitati este miezul, acestei interactiuni dintre utilizator si
componenta. Implementarea ei poate fi urmarita mai jos:
...
try
{
BeanInfo bi = Introspector.getBeanInfo(target.getClass());
properties = bi.getPropertyDescriptors();
}
catch (IntrospectionException ex) {
error("PropertySheet: Nu se poate inspecta componenta", ex);
return;
}
editors
= new PropertyEditor[properties.length];
values
= new Object[properties.length];
views
= new Component[properties.length];
labels
= new Label[properties.length];
EditedAdaptor
adaptor = new EditedAdaptor(frame);
for
(int i = 0; i < properties.length; i++) {
String name = properties[i].getDisplayName();
Class type = properties[i].getPropertyType();
Method getter = properties[i].getReadMethod();
Method setter = properties[i].getWriteMethod();
...
Se
poate observa cum se creeaza un obiect care sa interogheze obiectul
properties = bi.getPropertyDescriptors();
Dupa
cate cum se determina proprietatile, tipul lor, si metodele de scriere si
citire a lor:
String name = properties[i].getDisplayName();
Class type = properties[i].getPropertyType();
Method getter = properties[i].getReadMethod();
Method setter = properties[i].getWriteMethod();
Obiectul
setter contine, metodele de setare a proprietatilor, iar obiectul getter
contine metodele de citire a proprietatilor.
O
imagine a modificari proprietatilor unui Javins, mai exact a unui JaviButton,
se pot urmari in figura de mai jos:
Fig:
2.2.3.1 Modificarea proprietatilor unui Javins
2.2.4. Salvarea
interfetei
Metoda
pe care se bazeaza salvarea interfetei este serializarea, mai corect este o
implementare a metodelor de persistenta. Prin intermdiul persistentei starea
unui obiect se poate ingheta, astfel durata de viata a obiectului poate depasi
cu mult durata de viata a programului care la creat.
Aceasta
metoda este folosita si pentru transmitea interfetelor, unei aplicatii care ii
realizeaza infatisarea, si porneste functionalitatea obiectelor.
Ideea
de baza este sa se initializeze componentele cu valorile dorite, adresa sursei
de semnal, portul pe care se va constitui ca server de semnal, numele,
culoarea, dimensiunea, pozitia si multe altele, si sa se inghete componenta
prin serializare, pastrindu-si astfel, valorile setate de noi in editorul de
interfete. La rulare, aceste componente vor fi dezghetate, continind in
continuare obiectele setate de noi, si pe urma puse in functionare.
Metoda care realizeaza salvarea este prezentata in listingul de mai jos:
protected
void saveInterface(String type) {
if (workframe == null) {
new Message (this, "Error", "Nothing to Save");
return;
}
if (savePathFileName == null || type == "Save As") {
FileDialog fd = new FileDialog( this
,"Save Interface As"
, FileDialog.SAVE);
fd.setFile(saveFileName);
fd.setDirectory(".");
fd.show();
if ((saveFileName = fd.getFile()) == null) return;
savePathFileName = fd.getDirectory() + fd.getFile();
}
Vector vect = new Vector();
JavinstWrapper jwr;
Component [] cop = workframe.getComponents();
jwr = new JavinstWrapper(workframe.getSmartPanelProp().getSmartProp(),
"SmartProp");
vect.addElement(jwr);
for (int i = 0; i < cop.length; i++) {
if (cop[i] instanceof Wrapper) {
((Wrapper)cop[i]).prepareSaveBean();
jwr = new JavinstWrapper(((Wrapper)cop[i]).getBean(),
((Wrapper)cop[i]).getBeanName());
vect.addElement(jwr);
}
}
(new InterfaceSaver(vect, savePathFileName)).saveInterface();
for (int i = 0; i < cop.length; i++)
if (cop[i] instanceof Wrapper)
((Wrapper)cop[i]).restoreSaveBean();
}
Se
poate observa ca la salvare pentru fiecare componenta se apeleaza metoda din
wrapperul ei
prepareSaveBean,
aceasta metoda realizeaza, serializarea componentei, pe care o inglobeaza.
2.3. Incarcatorul
de interfete
Are
menirea de a incarca, afisa si pune in functionare o interfata.
Numele
clasei, care realizeaza aceasta este
SmartApplet.java
Poate
functiona in doua moduri:
- Ca
aplicatie de sine statatoare, caz in care la rulare se specifica, numele
fisierului de interfata pe care sa-l foloseasca.
- Ca
applet, caz in care se specifica, sub forma de tag parametru, numele
interfetei, pe care sa-l foloseasca.
Functionalitatea
lui descrisa pe scurt, este sa incarce componentele, serializate in cadrul
fisierului de interfete, sa le pozitioneze, respectiv afiseze, si in caz ca
sunt componente capabile de a rula intr-un fir de executie, sa le creeze un fir
de executi, si sa-l lanseze.
Un
fisier de interfata are urmatoare structura:
Fig:
2.3.1 Structura unui fisier de interfete
Codul
de incarcare a componentelor se poate urmari mai jos:
private
void loadJavinst() {
try{
ObjectInputStream ois;
if (!file) {
remoteTargetLocation = getCodeBase().toString() +
getParameter("InterfaceFile");
URL where = new URL(remoteTargetLocation);
ois = new ObjectInputStream(where.openStream());
}
else {
File where = new File(remoteTargetLocation);
ois = new ObjectInputStream(new FileInputStream(where));
}
int dim = ois.readInt();
comp = new Vector(dim);
for (int j = 0; j < dim; j++) {
JavinstWrapper wrp = (JavinstWrapper) ois.readObject();
comp.addElement(wrp);
}
JavinstWrapper wr = (JavinstWrapper) comp.elementAt(0);
obj = (SmartProp)wr.getJavinstObject();
setBounds(0,0,obj.getInterfaceWidth()+10, obj.getInterfaceHeight()+10);
resize(new Dimension(obj.getInterfaceWidth()+10,
obj.getInterfaceHeight()+10));
} catch(Exception e){
e.printStackTrace();
}
}
Se
poate observa diferenta, intre rularea ca aplicatie si rularea ca applet,
functional este vorba de locatia de unde se incarca fisierul de interfete, un
applet, desi se executa local, are resursele situate la distanta, si deci
fisierul trebuie, incarcat prin specificarea, si cu folosirea unei resurse URL.
In cazul rulari ca aplicatie de sine statatoare, el se cauta pe sistemul de
fisiere local.
Codul
sursa pentru pornirea, componentelor se poate urmari mai jos:
for
(int i = 1; i< components.size(); i++) {
jwr = (JavinstWrapper)javinst.elementAt(i);
Component com = (Component)jwr.getJavinstObject();
add(com);
if (com instanceof Runnable ) {
System.out.println("E runable");
if (com instanceof BaseJavins) {
System.out.println("Startimg thread for class: " +
com.getClass().getName());
Thread runthread = new Thread((Runnable)com);
runthread.start();
}
else {
Method met[] = com.getClass().getDeclaredMethods();
for (int j = 0; j < met.length ; j++){
if (met[j].getName().equals("run") || com instanceof
BaseJavins) {
System.out.println("Startimg thread for class: " +
com.getClass().getName());
Thread runthread = new Thread((Runnable)com);
runthread.start();
}
}
}
}
Verificarea
daca este o componenta multifir de executie se face prin doua moduri. Primul
verifica, daca la baza implementeaza, interfata de
Runnable,
interfata, pe care trebuie sa o implementeze orice fir de executie. Cea de a
doua metoda, verifica, utilizind reflectia daca componenta are o metoda
denumita,
run.
2.4. Generatorul
de semnal
Pentru
a demonstra functionarea, pachetului JaRPE, s-a implementat un generator de
semnal, care poate fi considerat un proces. Acesta distribuie semnalele si
comenzile sale facind astfel posibil, controlul lui de la distanta.
Pentru
a demonstra facilitatile instrumentelor virtuale, se folosesc aceleasi
componente care sunt folosite la elaborarea interfetei procesului si anume
- JaviDiagram
– pentru afisarea semnalului
- JaviMultiPos
– pentru selectarea tipului de semnal dorit
- JaviButton
- in calitate de buton de retea
Modul
cum arata
generatorul se poate vedea in figura de mai jos:
Fig:
2.4.1 Interfata generatorului de semnal
Semnalul
este generat de catre o clasa numita generator, care in functie de o comanda
primita este capabila sa genereze un stream de date care sa reflecte un semnal
sinusoidal, dreptunghiular sau triunghiular. Implementarea acestui generator se
poate urmari mai jos:
public
int getNextValue() {
switch (type) {
case 1: if (cnt > amplit) desc = true;
if (cnt < amplit*-1) desc= false;
if (desc) cnt--;
else cnt++;
break;
case 2: cnt++;
if (cnt > perio) cnt = 0 ;
if (cnt == 0) return 0;
if (cnt < perio/2 && cnt != 0) return amplit;
if (cnt == perio/2 ) return 0;
if (cnt > perio/2 && cnt < perio) return
amplit*-1;
if (cnt == perio) { cnt = 0; return 0; }
break;
case 3: ++cnt;
return ((new Double(Math.sin(cnt*Math.PI/70)*
amplit)).intValue());
}
return cnt;
}
Acesta
ruleaza ca un thread separat, pentru a se permite generarea continua a
semnalului. De asemenea el implementeaza,
localSignalSource
pentru a asigura semnal direct Javins-ului care se ocupa de afisarea semnalului.
Un
exemplu de implementare a unui controler de proces, realizat cu instrumente
virtuale, si facilitatile Javins-urilor se pot urmari prin modul de
implementare a acestui simulator:
//
JaviButton
javiButtonThread = new Thread(javiButton);
javiButton.setActionDisplayer(actdis);
javiButton.addActionListener(this);
javiButton.setServerPort(3000);
javiButton.setServer(true);
javiButton.setLocalSignalSource(javiButton);
javiButton.initializeConections();
javiButton.setConected(true);
javiButtonThread.start();
Aceasta
este sectiunea de creare a butonului de retea. Se poate observa, ca se seteaza
a fi server, prin metoda
setServer(true),
urmind a receptiona clienti la portul 3000.
//
JaviMultiPos
javiMultiPosThread = new Thread(javiMultiPos);
javiMultiPos.setActionDisplayer(actdis);
javiMultiPos.addActionListener(this);
javiMultiPos.setServerPort(3001);
javiMultiPos.setServer(true);
javiMultiPos.setLocalSignalSource(javiMultiPos);
javiMultiPos.initializeConections();
javiMultiPos.setConected(true);
javiMultiPosThread.start();
Aceasta
este sectiunea de creare a butonului multipozitional. Se poate observa, ca se
seteaza a fi server, prin metoda
setServer(true),
urmind a receptiona clienti la portul 3001.
//
JaviDiagram
javiDiagramThread= new Thread(javiDiagram);
javiDiagram.setLocalSignalSource(generator);
javiDiagram.setActionDisplayer(actdis);
javiDiagram.setServerPort(3002);
javiDiagram.setServer(true);
javiDiagram.initializeConections();
javiDiagram.setConected(true);
javiDiagramThread.start();
}
Aceasta
este sectiunea de creare a elementului care afiseaza semnalul. Se poate
observa, ca se seteaza a fi server, prin metoda
setServer(true),
urmind a receptiona clienti la portul 3002. Tot aici se poate observa cum se
seteaza, sursa de semnal al instrumentului virtual, ca fiind obiectul
generator.
2.5. Concluzie
Un
programator bun este capabil sa implementeze o specificatie in orice limbaj de
programare. Problema se pune insa, cu cite resurse irosite, timp, bani, nervi.
Astfel un limbaj de programare trebuie sa ajute programatorul in munca sa,
oferindu-i metodologii, tehnici si librarii de functii bine organizate si
acoperitoare pentru o multitudine de domenii. Limbajul Java, face parte din
acele limbaje de programare care sa asigure suport puternic pentru
implementarea aplicatilor din cele mai diverse domenii, asigurind in acelasi
timp o serie de tehnologii de ultima ora, printre care putem aminti:
programarea orientata pe obiecte, independenta de platforma, suport pentru
programarea distribuita si pentru multitasking, mecanisme deosebite de tratare
a exceptiilor, suport pentru aplicati multilingve, suport pentru baze de date,
mecanisme de securitate si criptografie puternice, si multe, multe altele.
Controlul
proceselor de la distanta si instrumentatia virtuala sunt doua dintre domeniile
automatici in curs de dezvoltare, si care vor avea un impact destul de puternic
asupra ei si in speta asupra informaticii industriale, de acea sa decis, ca
partea practica a acestui proiect, sa demonstreze beneficiile pe care le aduc
aceste tehnologii. Prin intermediul instrumentelor virtuale, dezvoltate sa
reusit implementarea unui simulator de proces in doar citeva sute de lini, si
citeva ore de munca. Bineinteles proiectul poate fi dezvoltat in continuare, el
reprezentind momentan mai degraba un proiect pedagogic decit o implementare
orientata spre performante. Ca o prima directie de dezvoltare se poate
implementa mecanisme de securitate si autorizari, canale de comunicatie
criptate si suport pentru baze de date.
In
momentul de fata, in urma cercetarilor facute de autor, asemenea proiecte de
aplicatii sunt in curs de dezvoltare doar in cadrul a doua organizatii sau
companii, este vorba de NASA si de Wizcon, ambele din Statele Unite ale Americi.