aspektisuuntautunut€ohjelmointi€€ja … ·...
TRANSCRIPT
ASPEKTISUUNTAUTUNUT OHJELMOINTI JA
OHJELMISTON MODULARISOINTI
Juhani SannikkaErikoistyöTietojenkäsittelytiedeKuopion yliopistontietojenkäsittelytieteen laitosMaaliskuu 2005
KUOPION YLIOPISTO, informaatioteknologian ja kauppatieteiden tiedekuntaTietojenkäsittelytieteen koulutusohjelma
JUHANI SANNIKKA, Aspektisuuntautunut ohjelmointi ja ohjelmiston modularisointiErikoistyö, 73 s., 12 liitettä (62 s.)Erikoistyön ohjaaja: FM Harri KarhunenErikoistyön tarkastaja: FM Heli LintulaHuhtikuu 2005
Avainsanat: aspectj, modularisointi, autentikointi, auktorointi, transaktio
AspectJohjelmointikieli on Javakielen laajennos, joka tarjoaa myös aspektisuuntautuneen ohjelmoinnin työkalun. Se sisältää AspectJkääntäjän, jonka tuottamaa tavukoodia tavallinen Javan virtuaalikone ymmärtää. Aspektien koodauksessa käytetäänhyväksi luokissa toteutettua perustoiminnallisuutta.Join point tarkoittaa luokissa toteutettua ohjelman kohtaa, kuten metodia, konstruktoria tai ohjelman kontrollivirtaa, jossa niitä suoritetaan. Pointcut on aspektissa esiteltyja määritelty ohjelmalohko, joka sieppaa halutut ohjelman join point –kohdat suoritettavaksi. Sieppaaminen voi perustua luokan tai metodin nimeen tai tyyppiin. Niidenkuvaamiseen voidaan käyttää reflektointia. Pointcut –lohkojen valitsemien join point kohtien suorituksia voidaan ohjeistaa advicemäärittelyillä. Advicelohkon koodi täydentää join point kohtien toimintoja. Advicelohkon koodi voidaan suorittaa niitäennen, niiden jälkeen tai se voi korvata alkuperäisen toiminnon kokonaan.AspectJohjelmointikieli on aspektisuuntautunut ohjelmointikieli, joka mahdollistaaluokkien toiminnallisuuden koostamisen ja käsittelemisen kokonaisuuksina, jotkamuodostavat ohjelman kannalta poikkileikkauksellisia tehtäviä tai ominaisuuksia.Pelkkänä luokkatoteutuksena näiden tehtävien koodi on vaikeasti hallittavissa, koskase levittäytyy laajalle ohjelmistoelementtien koodin sekaan. Aspekti auttaa poikkileikkauksellisten tehtävien modularisoinnissa, jolloin ohjelman rakenne selkiytyy.Ohjelmiston perustoiminnallisuus on paremmin hallittavissa ja myös muutokset aspektien muodostamaan modulaariseen rakenteeseen on helpompi toteuttaa, kuin luokkiin levitetyn koodin muutokset. Aspekteja voidaan määritellä myös ohjaamaan ohjelmistokehityksen koodaustapoa. Tällaiset aspektit saavat kääntäjän tulostamaan varoituksia tai estämään väärän koodauksen.Tässä raportissa kerrotaan aspektisuuntautuneen ohjelmoinnin keinoista toteuttaapoikkileikkauksellisten tehtävien, kuten autentikointi, auktorisointi ja transaktiot modulaarisina uudelleen käytettävinä ratkaisuina. Raportissa kerrotaan myös ratkaisuissakäytettävistä suunnittelumalleista.Luvussa 1 esitellään aspektisuuntautuneen ohjelmointitavan historiaa ja käsitteistöä.Luvussa 2 esitellään AspectJkieli, joka toteuttaa aspektisuuntautuneen ohjelmoinninympäristön. Luvussa 3 käsitellään esimerkkejä suunnittelumallien toteuttamiseksiaspekteina ja luvussa 4 säikeiden käsittelyä modulaarisesti aspektien avulla. Luvussa5 käsitellään järjestelmän autentikoinnin ja auktorisoinnin modulaarisia ratkaisuja.Luvussa 6 esitetään erilaisia aspektisuuntautuneen ohjelmoinnin mahdollisuuksiatransaktion modulaariseen toteutukseen. Lukujen 36 esimerkit perustuvat AspecJ inAction –teokseen, jonka on kirjoittanut R. Laddad.
3
Sisällysluettelo
1 ASPECT ORIENTED PROGRAMMING............................................................. 51.1 Oliosuuntautuneisuuden ohjelmoinnin kehittyminen...................................... 51.2 AOPtekniikan tavoitteet ............................................................................... 61.3 Näkymien muodostaminen ja sovelluksen ominaisuudet................................ 81.4 AOP:n vaikutus modularisointiin................................................................... 91.5 AOPkielinen implementointi ...................................................................... 12
2 ASPECTJ............................................................................................................ 152.1 Join point..................................................................................................... 152.2 Pointcut ....................................................................................................... 172.3 Advice......................................................................................................... 19
3 SUUNNITTELUMALLIT .................................................................................. 223.1 Workerobjektin luonti suunnittelumalli .................................................... 22
3.1.1 Workermetodin palauttama arvo ......................................................... 24
3.1.2 Workerobjektin kontekstin hallinta ..................................................... 25
3.2 Madonreikä suunnittelumalli ....................................................................... 26
3.2.1 Madonreikä suunnittelumallin periaate................................................. 26
3.2.2 Madonreikämallin kaava...................................................................... 27
3.3 Poikkeuksien käsittely ................................................................................. 30
3.3.1 Tehtäväkohtaisen poikkeuksen käsittely aspektissa .............................. 31
3.3.2 Liiketoimintokohtaisen poikkeuksen käsittely aspektissa ..................... 32
3.3.3 Poikkeuksen taltiointi aspektissa .......................................................... 32
3.4 Osallistujasuunnittelumalli ......................................................................... 33
3.4.1 Osallistujasuunnittelumallin kaava...................................................... 36
4 SÄIETURVALLISUUS...................................................................................... 38
4.1 Swing ja yhden säikeen sääntö..................................................................... 38
4.1.1 Perinteinen ratkaisu.............................................................................. 39
4.1.2 Säieturvallisuus aspektina .................................................................... 41
4
4.1.3 Objektin lukitusmalli aspektina............................................................ 43
5 AUTENTIKOINTI JA AUKTORISOINTI.......................................................... 46
5.1 Pankkijärjestelmä ........................................................................................ 465.2 JAASpohjainen autentikointi perinteisellä tavalla ....................................... 48
5.2.1 LoginContext –objektin luonti ............................................................. 48
5.2.2 Takaisinkutsun käsittelijä..................................................................... 49
5.2.3 Kirjautumisen konfiguraatiotiedosto .................................................... 49
5.3 Autentikointi AspectJratkaisuna ................................................................. 505.4 Auktorisointi ............................................................................................... 52
5.4.1 JAASpohjainen auktorisointi luokkatoteutuksena ............................... 52
5.4.2 JAASpohjainen auktorisointi AspectJtoteutuksena ............................ 53
6 TRANSAKTIO................................................................................................... 55
6.1 Ydin tehtävien toteutus................................................................................ 556.2 Transaktion perinteinen ratkaisu .................................................................. 566.3 AspectJkielinen transaktion ratkaisu........................................................... 59
6.3.1 JDBCTransactionAspectaspektin koodin selitys ................................. 61
6.3.2 BankingTransactionAspectaspektin koodin selitys.............................. 63
6.4 Yksi transaktio – useita alijärjestelmiä ......................................................... 63
6.4.1 TransactionParticipantAspectaspektin selitys...................................... 64
6.4.2 Usean alijärjestelmän transaktion toteuttava perusaspekti..................... 67
6.4.3 Muutetun JDBCTransactionAspectaspektin koodin selitys ................. 68
6.5 JTApohjainen transaktion hallinta .............................................................. 697 YHTEENVETO.................................................................................................. 71
LÄHTEET.................................................................................................................. 72
5
1 ASPECT ORIENTED PROGRAMMING
1.1 Oliosuuntautuneisuuden ohjelmoinnin kehittyminen
Tietojenkäsittelyjärjestelmät ja ohjelmointikielet ovat kehittyneet konekielistä (assem
bly) käsitteiden ja kaavojen muodostamiseen ja kääntämiseen, proseduurien, rakentei
den, toimintojen, logiikan ohjelmointiin ja abstraktien tietotyyppien käyttöön. Tämä
ohjelmointitekniikan kehitys on parantanut mahdollisuuksia käsitellä lähdekoodissa
erillisiä tehtäväkonaisuuksia (concerns).
Nykyisin vallitsevassa oliosuuntautuneessa ohjelmoinnissa (OOP, Object Oriented
Programming) ohjelman käsitteet ja toiminnot on ratkaistu olioiden ja niiden toimintoja
ohjaavan koodin avulla. Olioiden käyttäytyminen ja data muodostavat abstrahoidut (ja
fyysiset) käsitteet ohjelman suorituksessa. Olioohjelmoinnilla pysytään ratkaisemaan
hyvin monimutkaisia ongelmaalueita, mutta silläkin on eräitä rajoitteita. Esimerkiksi
joitakin ohjelman suorituksessa vaadittuja toimintoja ei voida koodata yhden ohjelman
osan suoritettavaksi, vaikka ne sille luontevasti kuuluisivatkin. OOteknologiassa on
huomioitava globaaleja rajoitteita (constraints) ja kaikkialla yleisesti vaikuttavaa (pan
demic) käyttäytymistä, joita on vaikeaa hallita paikallisesti. Tämä voi vaikeuttaa ongel
ma alueiden eriyttämistä ja edellyttää käyttökohteen toiminnan alan mukaista sovelta
mista (domainspecific).
Postobject programming (POP) on olioohjelmoinnin seuraava kehitysvaihe, joka laa
jentaa sen ilmaisu kykyä. POP on edesauttanut esimerkiksi seuraavien alojen kehitty
mistä:
toiminnan alan erityiskielet (domainspecific language),
generatiivinen eli muodostava ohjelmointi (generative programming),
geneerinen eli yleistävä ohjelmointi (generic programming),
sääntöjä ja rajoitteita käyttävät sääntökielet (constraint languages),
6
luokkien peilaus (reflection) ja metaohjelmointi (metaprogramming) ,
ominaisuussuuntautuneiden ohjelmistojen kehittäminen (featureoriented deve
lopment),
näkymät ja näkökulmat (views, viewpoints), sekä
epäsynkroninen viestin välitys (asynchronous message brokering).
Seuraavassa luvussa kerrotaan AOP:sta (AspectOriented Programming), joka on eräs
POPteknologia. AOP tekniikka parantaa ohjelmistojen toteutuksen hallittavuutta, koska
ohjelmiston eri kokonaisvaatimuksia tai huolenaiheita (concerns) määritellään erillisinä
ohjelman osina. Näille osille määritellään kuvauksia niiden yhteyksistä (relationship),
joiden mukaisesti ohjelman osia koostetaan (weave) yhdeksi kokonaisuutena toimivaksi
sovellukseksi.
1.2 AOPtekniikan tavoitteet
Ohjelman toiminnot (concerns) voivat huolehtia tehtävistä, jotka muodostuvat käsitteis
töltään laajoista päättelysäännöistä (highlewel notions), kuten ohjelmiston turvallisuut
ta ja laatua palvelevat tehtävät, tai ne voivat huolehtia myös pienemmistä tehtävistä
(lowlewel notions), kuten ajureista ja puskuroinnista. Ohjelman osilla voi olla toimin
nallisia piirteitä, esimerkiksi ne voivat toteuttaa liiketoiminnallisia palveluja (business
rules). Ohjelman osilla voi olla myös eitoiminnallisia piirteitä, jotka ohjaavat järjestel
mää, kuten synkronointi ja transaktioiden hallinta. Vertauksellisesti voidaan sanoa, että
kun OOP pyrkii hyödyntämään luokkien yhteyttä ja perintäpuuta, AOP pyrkii toteutta
maan ohjelman toiminnot erillisistä primääriluokkaisista (firstclass) elementeistä, jot
ka käynnistyvät suoraan horisontaalisesta oliorakenteesta.
Rakenteellisesti jotkin tietojärjestelmän toiminnot voidaan toteuttaa kätevästi yhdessä
rakenneosassa, kun taas joidenkin toimintojen toteutus levittäytyy monelle rakenne
7
osalle. AOP keskittäytyy mekanismeihin, joilla näitä eri rakenneosille levittäytyviä toi
mintoja voidaan yksinkertaistaa. AOP:n päämääränä on modularisoida toteutettavien
ohjelmistojen rakenne, jolloin pienen muutoksen tekeminen loogisella tasolla ei aiheuta
koodin tai suunnittelun tasolla muutoksia moniin eri paikkoihin ohjelmistossa [ElF01].
Ohjelmiston näkymävaatimukset (aspectual requirements) ovat ongelmaalueita (con
cerns), joita voidaan ratkaista levittämällä toteutus osissa usealle rakenneosalle. Esi
merkkejä tällaisista ongelmaalueista ovat:
Synkronointimenetelmät, jotka vaativat melkoisen joukon operaatioita varmis
tamaan yhdenmukaisen lukitusprotokollan,
Monimutkaisten oliograafien läpikäynti, johon tarvitaan globaalia informaatiota.
Laskutus mekanismit, joilla hallitaan veloituksia.
Vikasietoiset mekanismit, jotka luovat toistuvasti yhdenmukaisia kopioita.
Sekä palvelun laatua varmistavat toiminnot, jotka vaativat järjestelmän priori
teettien määrittelyä, kuten autentikointi, jonot ja käyttöoikeudet.
Nämä edellä luetellut toiminnot on perinteisesti toteutettu aliohjelmilla, eikä AOP:kään
hylkää tätä ratkaisua täysin. Perinteisessä toteutuksessa poikkileikkauksellista toimintoa
käsittelevän aliohjelman koodista tulee suttuista ja vaikeasti hallittavaa, kun sitä joudu
taan toteuttamaan useiden ohjelmistoelementtien koodin seassa. Tämän ongelman kor
jaamiseksi AOP tarjoaa aspektin. Se on mekanismi, joka kätkee alirutiinit ja perinnän
sekä paikallistaa käsiteltävän toiminnon koodin ohjelmiston useista eri osaalueista.
AOP kehitysympäristö sisältää punontamekanismin (weaver), joka punoo aspektit ja
sovelluksen peruskoodin koossa pysyväksi järjestelmäksi.
AOPkehitysympäristö sisältää sisäisen (implisiittisen) kutsumekanismin, joten lähde
koodin toteuttajan kanssa ei tarvita yhteistyötä, jotta tiedetään kuinka lähdekoodia voi
daan kutsua, kuten aliohjelmia käytettäessä. Kun erilaisia toimintoja voidaan eriyttää
muusta ohjelmiston kehittämisestä, voidaan yksinkertaistaa ja nopeuttaa järjestelmän
8
kehittämistä. Järjestelmistä saadaan myös helpommin ymmärrettäviä, muunneltavuus
paranee myös asiakaskohtaisesti samoin kuin uudelleen käytettävyys.
Aspektin muodostava koodi yhdistää poikkileikkaavasti ohjelmassa huolehdittavia asi
oita (crosscutting concerns). Tämä modulaarinen koodi on helpompi käsitellä, kuin
perinteisen ohjelmiston eri osiin tehty koodi. Myös ohjelmiston varsinainen kohdetta
käsittelevä koodi on helpommin ymmärrettävissä, koska ohjelmoija voi kutsua aspektin
koodissa esimerkiksi erikseen toteutettuja hajautusalgoritmeja, autentikointia, avaus
käskyjä (access control), synkronointia, salausta, automatisointia (redundancy). Aspektit
voivat olla uudelleen käytettäviä ja ne voivat olla liittimiä (connector) muihin kom
ponentteihin.
1.3 Näkymien muodostaminen ja sovelluksen ominaisuudet
Aspekti voi määrittelynä sisältää assosiaation muista ohjelman käsitteistä, kuten muut
tujat ja metodit, tyyppien esittelyt, poincutmäärittelyt ja advicemäärittelyt. AOP
tekniikassa aspekti muodostetaan valitsemalla ohjelman perustoiminnallisuuden luokis
ta join point kohdat, joiden suoritus palvelee kollektiivisesti ohjelman suoritusta
[ElF01].
Join point –kohdat ovat ohjelman ydintoteutuksen koodia, jonka toiminnallisuutta as
pekti hyödyntää. Ne voidaan ryhmitellä luonteensa mukaisesti suoritus , kutsu ja ken
tän käytön tyyppisiin join point –kohtiin:
Aspektissa voidaan viitata join pointkohtaan, joka suorittaa metodin, muuttujan alusta
jan, konstruktorin, staattisen alustajan, käsittelijän tai objektin alustajan. Samoin aspek
tissa voidaan viitata join point kohtaan, joka kutsuu metodin, konstruktorin ja objektin
esialustuksen. Join point kohta voi myös viitata kentän arvoon tai asettaa sen [LaC03].
Pointcutmäärittelyt voivat sisältää viittauksia useisiin join point kohtiin, jotka vaikut
tavat aspektin muodostukseen eri tavoin. Pointcut määrittelyn kautta aspektien koodi
9
on vuorovaikutuksessa järjestelmän muun koodin kanssa. Pointcut voidaan parametri
soida haluttua käyttötarvetta tai käyttäjää varten.
Pointcut kohdat ovat useimmiten dynaamisia eli voivat viitata ohjelmassa meneillään
oleviin tapahtumiin ja muuttavat ohjelman käyttäytymistä. Pointcutlohkot sisältävät
määrittäjän (pointcut designator eli pcd). Niissä voidaan määritellä halutut join point –
kohdat ja ehtoja niiden suorittamiselle.
Pointcut kohdan lisäksi aspektissa määritellään ohjeita (advice), nämä yhdessä vaikut
tavat ohjelman etenemiseen käyttäen aspektissa toteutettuja palveluja järjestelmän koh
detoimintojen toteuttamiseen. Kun ohjelma etenee advicekohtaan, niiden koodi suorite
taan. Advicelohkon suoritus voi tapahtua ennen, jälkeen tai sen toiminnon sijaan mitä
lohkossa määritelty koodi kutsuu.
Tätä prosessia, joka saa aikaan sopivan advicelohkon suorittamisen kussakin liitokses
sa kutsutaan näkymän koostamiseksi (weaving). Pointcutmäärittelyt voivat olla myös
staattisia, jolloin ne vaikuttavat lähinnä vain sovelluksen rakenteen muodostumiseen ja
tukevat dynaamisten poikkileikkauksellisten tehtävien toteuttamista [LaC03].
Tulkin (interpreter) suorittaessa ohjelmaa, se kutsuu saavuttamassaan pointcutkohdassa
koostajaa (weaver), joka huolehtii join point, advice ja pointcutmäärittelyjen mukais
ten toimintojen liitosten valinnoista, niiden vaatimien ohjeiden kutsumisesta ja ohjeiden
mukaisesti valittujen ohjelman osien suorittamisesta [ElF01].
Koostesäännöt (weaving rules) määrittelevät ohjelman etenemisen vaiheissa ohjelmassa
suoritettavia toimintoja. AspectJkielessä näitä sääntöjä ei kirjoiteta ydintoimintojen
toteutukseen, vaan ne sisältyvät näkymäkoodiin ja kääntäjän sääntöihin. [Lad03,
ElF01].
1.4 AOP:n vaikutus modularisointiin
Perinteisessä olioohjelmoinnissa selkeästi eriytettyjä ydintoimintoja suorittavat mo
duulit voidaan toteuttaa rajapintojen avulla ja kytkeä löysästi toisiinsa. Sovellukseen
10
pitää kuitenkin toteuttaa toimintoja, jotka vaikuttavat sen useissa osissa, niin palvelimel
la kuin asiakassovelluksessa. Näitä toimintoja kutsutaan poikkileikkauksellisiksi tehtä
viksi (crosscutting concern).
Esimerkiksi käyttäjän auktorisointi (authorization) moduuli voidaan toteuttaa rajapin
nalla, jonka käyttö ei sido tiukasti asiakassovelluksen toteuttamista. Ohjelmoijan ei tar
vitse edes tietää auktorisointipalvelun tarkkaa toteutusta käyttäessään sitä ja palvelu
voidaan myös vaihtaa toiseen toteutukseen. Kuitenkin jokaisen toteutukseen sisältyy
palvelun käyttöön vaadittavaa koodia, mikä sekoittuu peruslogiikan toteuttamaan koo
diin, joten palvelun vaihtaminen toiseen ei onnistu ilman suuria muutoksia järjestel
mään.
Kuva 1 esittää perinteisen olioohjelmoinnin tekniikalla toteutettuja pankkijärjestelmän
moduuleita; Tili, Tilinsiirronhallinta ja Tietokantamoduulit, sekä niiden toimintoja
kirjoittava Lokimoduuli. Lokimoduuli voidaan toteuttaa abstraktin API:n, joka piilottaa
yksityiskohtaisen toteutuksen. Kaikkien muiden moduuleiden täytyy kuitenkin sisältää
tuota API:a kutsuvaa koodia.
AOPtekniikkaa käytettäessä ydintoimintojen moduulit eivät sisällä APIkutsuja, eikä
niissä tarvitse olla tietoisia Lokimoduulista, joka Lokiaspektin kanssa huolehtii lokin
kirjoittamisesta, katso kuva 2. Jos tähän sovelluksen poikkileikkaukselliseen lokinkir
joitus tehtävään syntyy uusia vaatimuksia, tarvitsee muutoksia tehdä vain Loki
aspektiin, joka huolehtii tietojen sieppaamisesta ja kutsuu lokia kirjoittavaa API:a.
Asiakasmoduulit eivät sisällä lokin kirjoittamiseen liittyvää koodia.
11
Kuva 1. Moduulien toteutus perinteisellä tekniikalla. APIkutsut kytkevät
moduulit toisiinsa [Lad03].
Kuva 2. AOPtekniikalla toteutettu pankkijärjestelmän lokinkirjoitus. Aspekti määritte
lee sieppauskohdat ja kutsuu niitä suoritettaessa lokia kirjoittavaa API:a [Lad03].
Loki aspekti
Tietokantamoduuli
Tilisiirronmoduuli
Tili moduuliAutomaattisesti
muodostetut kutsut
Loki moduliAPI kutsut
Loki moduuli
Tietokantamoduuli
Tilisiirronmoduuli
Tili moduuliAPI kutsut
12
Sovellusten poikkileikkauksellisten tehtävien modularisointi tekniikoita on useita. Esi
merkiksi Enterprise JavaBeans (EJB) –arkkitehtuuri yksinkertaistaa hajautettujen palve
linpuolen sovellusten luomista ja laajojen tehtävien käsittelyä, kuten turvallisuuden (se
curity), hallinta (administration), suorituskyky (performance) sekä säiliöinnin pysyvyys
(containermanaged persistence). Javapapujen kehittäjät voivat keskittyä liiketoiminta
logiikkaan tai toisaalta asennusnäkymässä pavun datan kartoittamiseen tietokannan da
tan kanssa. Näin EJBkehyksessä voidaan eriyttää erillisiksi tehtäviksi liiketoimintalo
giikka ja asennuskuvauksen (deployment descriptor) avulla datan kartoitus pavun ken
tistä tietokannan sarakkeisiin. Asennuskuvaus on kartoituksen XMLtiedosto [Lad03].
Toinen poikkileikkauksellisten tehtävien modularisointitekniikka on dynaamisten pro
xien käyttäminen proxysuunnittelumallin mukaisesti. Tämä on monimutkainen tek
niikka, jota ei käsitellä tässä raportissa.
Modularisoimattomuus aiheuttaa järjestelmän koodin sekaisuuden, jos yksittäinen mo
duuli on toteutettu niin, että se huolehtii useista eri tehtävistä samanaikaisesti. Yhteen
tehtävään liittyvää koodia joudutaan myös levittämään useisiin moduuleihin, joten oh
jelmiston muutokset ovat työläitä ja alttiita virheille.
1.5 AOPkielinen implementointi
AOPmetodologian mukainen järjestelmän kehittäminen etenee periaatteeltaan tavan
omaisesti. Tunnistetaan tehtävät (concern), implementoidaan ne ja muodostetaan niistä
yhdistämällä lopullinen järjestelmä. Tehtävien tunnistamiseksi vaatimukset jaotellaan
yksittäisiin ydintehtäviin (core concerns) ja järjestelmätason poikkileikkauksellisiin
tehtäviin (crosscutting concerns). Ensin mainittuja ovat esimerkiksi ydin liiketoiminnan
tehtävät ja järjestelmätasolla monen moduulin alueelle vaikuttavia ovat esimerkiksi aut
horisointi, lokin kirjoitus, pysyvyys, säieturvallisuus ja puskurointi.
Seuraavassa vaiheessa jokainen näistä tehtävistä implementoidaan itsenäisesti. Ydin
tehtävien toteutukseen voidaan käyttää tavanomaista olioohjelmointi tekniikkaa, eli
13
koodataan perustoiminnallisuuden ja rakenteet sisältävät ohjelmointikieliset rajapinnat
ja luokat.
Lopuksi määritellään koostamissäännöt aspekteille, jotka muodostavat modularisoinnin
yksiköt. Aspektien sisältämän informaation perusteella muodostuu lopullisen järjestel
män toiminnallisuus. Esimerkiksi aspektissa voidaan määritellä, että jokaisen sovelluk
sen operaation pitää varmistua asiakkaan oikeuksista ennenkuin sovellus etenee liike
toimintalogiikkaan [Lad03].
Kieliriippumaton AOPtoteutus muodostuu kaksivaiheisesti: ensin yhdistetään erilliset
tehtävät koostesääntöjen (weaving rules) mukaisesti. Koostesäännöt määritellään aspek
teissa. Ne voivat olla eri ohjelmointikielisiä toteutuksia kuin ydintehtävien toteutus.
Kääntävälle prosessorille voidaan antaa ohjeeksi eri lähdekoodisia käännöksiä lopulli
sen suoritettavan koodin muodostamiseksi, mikä vaatii kuitenkin työläitä käännösvai
heita.
Kuva 3 esittää aspektien ja ydintehtävien toteutuksen yhdistämisen koostamissääntöjen
mukaisesti järjestelmäksi. Tässä koostaja (weaver) sisältyy AOPkääntäjään (AOP
Compiler). Koostesäännöt (weaving rules) määrittelevät poikkileikkaukselliset tehtävät
ja antavat lisätoiminnallisuutta ydintehtävien toteutukseen, joten niiden lähdekoodiin ei
tarvitse tehdä muutoksia. Koostaminen ilmenee vain kääntäjän tuottamassa tavukoodis
sa. Ydintehtävien toteutus voi sisältää javalähdekoodia ja luokkatiedostoja. Järjestel
mä sisältää koostetun ryhmän luokkatiedostoja.
14
AOPkääntäjä
(AOP Compiler)Järjestelmä
Ydintehtävientoteutus
(Core concernimplementation)
Koostesäännöt(Weaving Rules)
Kuva 3. AOP kääntäjä toimii lopullisen järjestelmän koostajana (wiever) [Lad03].
Tässä raportissa perehdytään aspektien toteutukseen vain Javaympäristössä käyttäen
valmista AspectJkääntäjää. Ympäristön tarkempi selvitys on liitteessä 1.
15
2 ASPECTJ
AspectJohjelmointikieli on aspektisuuntautunut Javakielen laajennus, joten Java
kielinen ohjelma on myös kelvollinen AspectJohjelma. AspectJkääntäjä (compiler)
kääntää lähdekoodin luokkatiedostoiksi, jotka ovat yhdenmukaisia Javan tavukoodin
kanssa, joten Javan virtuaalikone pystyy suorittamaan nämä luokkatiedostot.
AspectJkäännöksessä toteutuvat dynaaminen ja staattinen poikkileikkauksellisuus.
Dynaaminen poikkileikkauksellisuuden (dynamic crosscutting) koostaminen tarkoittaa
uuden toiminnon tai käytöksen kutomista (weaving) ohjelman suoritukseen. Näin voi
daan ottaa käyttöön tai korvata ydintoteutuksen moduulien toimintoja ohjelman suori
tuksessa [Lad03].
Staattinen poikkileikkauksellisuuden (static crosscutting) koostaminen ei muuta ohjel
man käytöstä suorituksen aikana, vaan sen staattista rakennetta, kuten luokkia, rajapin
toja ja aspekteja. Esimerkiksi luokkiin ja rajapintoihin voidaan lisätä uusia metodeja tai
dataa, jotta luokkien tilan ja käytöksen avulla voidaan toteuttaa dynaamista poikkileik
kauksellisuutta. Staattista poikkileikkauksellisuutta ovat myös käännöksen aikaiset va
roitukset ja virheilmoitukset, joilla ohjataan moduulien toteuttamista.
Javakielen AspectJlaajennukset määrittelevät dynaamisen ja staattisen poikkileikka
uksellisuuden koostesääntöjä (weaving rules). AspectJlaajennuksissa käytetään join
point, pointcut ja advicemäärittelyjä, jotka ovat ohjelmallisia koostesääntöjä. Nämä
määrittelyt muodostavat moduuleja, jotka toteuttavat jonkin poikkileikkauksellisen teh
tävän sovelluksessa [Lad03].
2.1 Join point
Join point on tunnistettava kohta ohjelman lähdekoodissa ja suorituksessa. Se voi olla
metodin kutsu tai objektin jäsenen määrääminen (assignment). AspectJkielessä join
16
point on hyvin keskeinen, koska ne ovat ohjelman luokkatoteutuksen kohtia, joista
poikkileikkaukselliset toiminnot kerätään. Esimerkiksi seuraavassa luokan määrittelyssä
voi löytää kaksi join point –kohtaa: suoritettava credit()metodi ja _balance
instanssijäsenen käyttö.
public class Account {
…
void credit(float amount) {
_balance += amount;
}}
Koska join point on jokin kohta ajettavan ohjelman dynaamisten kutsujen graafissa, sen
käyttäytymistä voidaan muuttaa, esimerkiksi aspektin advicelohkon ohjeella. Jokaista
dynaamista join pointkohtaa vastaa jokin lähdekoodin nimetty osa. Erilaiset join point
kohtien lajit lähdekoodissa ja niitä vastaavat tunnisteet (signature) on lueteltu seuraa
vassa taulukossa. Näitä kohtia voidaan määrittää lähdekoodista joko tunnisteen perus
teella suoraan tai reflektoimalla [Lad03].
Taulukko 1. Join poin lajit ja niiden tunnisteet
Join pointlaji Tunniste lähdekoodissa (signature)
Metodin suoritus Metodi
Metodin kutsu Metodi
Konstruktorin suoritus Konstruktori
Kentän saanti (get) Kenttä
Kentän asetus (set) Kenttä
Advicelohkon suoritus
Alustus Vastaava konstruktori
Staattinen alustus Tyypin nimi
17
Esi alustus Vastaava konstruktori
Poikkeuksen käsittelijä Poikkeuksen tyypin nimi
Poikkeuksen heitto Poikkeuksen tyypin nimi
Synkronointi Lukitusobjektin tyypin nimi
Taulukko 1 esittää join point –lajit sekä tunnisteet, joiden perusteella näitä kohtia voi
daan määrittää lähdekoodista. Advicelohko määritellään aspektissa, jossa se ohjeistaa
join point –kohdan suoritusta [HiH04].
2.2 Pointcut
Pointcut on ohjelmarakenne, joka valitsemalla join pointkohdat ja kokoaa näiden koh
tien kontekstin. Esimerkiksi pointcut voi valita metodin kutsun, ja voi myös siepata me
todin kontekstin, kuten metodin kutsuman kohdeobjektin sekä metodin argumentit. Näin
pointcut määrittelee koostesäännön (weaving rule), jonka joint pointkohta tilannekoh
taisesti täyttää. Edellisen luvun 2.1 esimerkin jatkona voidaan kirjoittaa pointcut, joka
sieppaa credit()metodin suorituksen:
execution(void Account.credit(float))
AspectJ:n pointcutmekanismi sisältää join point –kohtien valintaoperaattoreita, joita
kutsutaan primitiivisiksi pointcut määrittäjiksi (primitive pointcut designator, pcd).
Pointcutlohkoissa nämä määrittäjät valitsevat sellaisia joinpointtyyppejä, joiden meta
datan kuvaus täsmää määrittäjän argumentteihin, katso taulukko 1.
Nämä argumentit voivat perustua joinpointkohdan nimeen (signature) tai tyypin kuva
ukseen (type pattern), joiden ilmaisemiseen voidaan käyttää jokerimerkkejä (wildcards).
Pointcutlohkon määrittäjät voivat tunnistaa myös toisen pointcutlohkon määrittelyn
joinpointkohdat ja liittää ne kontrollivirtaansa. Määrittäjä voi valita joinpointkohdat
myös niiden suorituksen kontekstissa käytettävien objektien ja argumenttien perusteella.
Näin ollen primitiiviset pointcut määrittäjät voidaan jakaa argumenttiensä perusteella
kolmeen categoriaan, katso taulukot 2, 3 ja 4. [LaC03]
18
Taulukko 2. Tyypin kuvaukseen tai nimeen perustuva pointcutlohkon määrittäjä
Määrittäjä (designator) Valitut join point kohdat
call(Signature) metodien ja konstruktorien kutsut
execution(Signature) metodien ja konstruktorien suoritus
initialization(Signature) objektin alustuksen suoritus
get(Signature) kentän viittaus
set(Signature) kentän asetus
staticinitialization(TypePattern) staattisen alustajan suoritus
within(TypePattern) kaikki tyypin mukaiset join pointkohdat
withincode(Signature) metodien ja konstruktorien määrittelyt
Taulukossa 2 luetellaan määrittäjät, jotka valitsevat joinpointkohdat nimen tai tyypin
kuvauksen perusteella [LaC03].
Taulukko 3. Pointcutperusteinen määrittäjä
Määrittäjä (designator) Valitut join point kohdat
cflow(pointcut) kaikki suorituksessa käytetyt joinpointkohdat jotkapointcut määrittää
cflowbelow(pointcut) kuten cflow, mutta ei sisällä argumenttinä olevapointcutlohkon join pointkohtia
Taulukossa 3 luetellaan määrittäjät, jotka valitsevat joinpointkohdat toisen pointcut
lohkon määrittelyn perusteella [LaC03].
19
Taulukko 4. Kontekstiperusteinen määrittäjä
Määrittäjä (designator) Valitut join point kohdat
this(TypePattern or Id) joinpointkohdat joissa thismääreellä sidottu objekti on tietyn tyypin ilmentymä
target(TypePattern or Id) joinpointkohdat joissa kutsu tai kenttäoperaatio kohdistuu tietyn tyyppisen objektinilmentymään
args(TypePattern or Id, ...) joinpointkohdat joiden argumentit täsmäävätmäärittäjän argumetteihin
Taulukossa 4 luetellaan määrittäjät, jotka valitsevat joinpointkohdat niiden suoritukses
sa käytettävien objektien ja argumenttien perusteella. Nämä sisältävät myös suorituksen
kontekstin [LaC03].
2.3 Advice
Advice –koodi suoritetaan siinä join pointkohdassa, joka pointcutmäärittelyssä on
valittu. Advicekoodi voidaan suorittaa ennen join pointkohtaa tai sen jälkeen, se voi
myös ohittaa kohdan tai korvata sen. Advicekoodi voi esimerkiksi kirjoittaa viestin
ennen kuin eri moduleissa olevat join point kohdat suoritetaan. Advicemäärittelyn
runko muistuttaa metodin runkoa, sillä se kapseloi suoritettavan logiikan metodiin siir
ryttäessä. Esimerkiksi seuraava advice tulostaa viestin ennenkuin suorittaa Account
luokan credit()metodin.
before() : execution(void Account.credit(float)) {
System.out.println(“ Veloitetaan”);}
Pointcut ja advice yhdessä muodostavat dynaamisen poikkileikkaussäännön. Pointcut
tunnistaa vaaditun join point –kohdan, ja advicelohkon ohjeet täydentävät join point –
kohtien toimintoja. Ohjeita voidaan määritellä suorituksensa ja käyttötarkoituksensa
mukaisesti seuraavasti:
20
before()advice suoritetaan ennenkuin ohjelman suoritus etenee käsiteltävänä ole
vaan join point kohtaan. Esimerkiksi:
before(): move() {
System.out.print(”lähtee”);}
after()returning ja after()throwing –advice suoritetaan, kun join point
kohta on palauttanut jotain tai heittänyt poikkeuksen. Pelkkä after()advice voidaan
suorittaa molemmissa tapauksissa. Esimerkki palautuksesta:
after(FigureElement fe, int x, int y) returning:
call (void FigureElement.setXY(int, int))
&& target(fe)
&& args(x,y) {
System.out.println(fe + “ moved to (“ + x +“,”+y+”)”); }
around()advice suoritetaan käsiteltävänä olevan join point kohdan sijaan. Join
pointkohdan alunperäistä toiminnallisuutta voidaan kutsua käyttämällä proceed() –
kutsua [HiH04].
Esittely (Introduction) on staattinen poikkileikkaava käsky, joka esittelee muutoksia
sovelluksen luokkiin, rajapintoihin ja aspekteihin. Se saa aikaan staattisia muutoksia
moduuleihin, mutta ei vaikuta suoraan niiden käytökseen. Esimerkiksi luokkaan voi
daan lisätä metodi tai kenttä. Esimerkiksi seuraavassa esittely ilmoittaa, että Account
luokka toteuttaa BankingEntityrajapinnan:
declare parents: Account implements BankingEntity;
Käännöksen aikaiset ilmoitukset (compiletime declarations) ovat staattisia poikki
leikkaavia ohjeita, joilla voidaan esimerkiksi ehkäistä väärää koodaustapaa. Esimerkiksi
voidaan ilmoittaa, että on virhe kutsua Abstract Window Toolkit (AWT)koodia
21
EJB:stä. Seuraava käännöksen aikainen ilmoitus saa kääntäjän varoittamaan, jos jostain
järjestelmän osasta kutsutaan Persistenceluokan save()metodia. Tällöin call()
pointcut sieppaa metodin kutsun.
declare warning : call(void Persistence.save(Object))
: “ Consider using Persistence.saveOptimized() “ ;
Aspekti (aspect) on yhtä keskeinen yksikkö AspectJkielessä kuin luokka on Java
kielessä. Aspektin koodi ilmaisee sekä dynaamisen että staattisen poikkileikkaavuuden
koostesäännöt. Aspekti yhdistää pointcut ja advicemäärittelyt, sekä ilmoitukset ja esit
telyt. Näiden AspectJelementtien lisäksi aspekti voi sisältää dataa, metodeja ja sisäluo
kan jäseniä, samalla tavalla kuten javaluokkakin. Edellä olevien esimerkkejä käyttäen
voidaan kirjoittaa seuraavanlainen aspekti:
public aspect EsimerkkiAspekti {
before() : execution(void Account.credit(float)) {
System.out.println(“ Tulostettava viesti”);
}
declare parents: Account implements BankingEntity;
declare warning: call(void Persistence.save(Object))
: “Consider using Persistence.saveOptimized()“ ;
}
Aspektissa voidaan määritellä sisäluokkia samoin kuin luokissa sisäaspekteja. Staattinen
aspekti voi esitellä luokalle uusia jäseniä. Aspektin instanssi voi ohjeistaa (advice) sekä
luokan että aspektin instanssin jäseniä [Ken99].
22
3 SUUNNITTELUMALLIT
Tässä luvussa tutkitaan Workerobjektin luonti , Madonreikä , Poikkeuksen esittely ja
Osapuoli –suunnittelumalleja. Malleja käytetään tässä raportissa kehitettävän sovelluk
sen toteutuksessa. Suunnittelumallien lähdekoodit ovat raportin sähköisessä liitteessä.
3.1 Workerobjektin luonti suunnittelumalli
Workerobjekti on luokanilmentymä, joka kapseloi metodin niin, että metodia voidaan
käyttää kuten objektia. Tuota metodia kutsutaan Workermetodiksi. Workerobjektia
voidaan kutsua, välittää toiseen paikkaan ja varastoida. Yleinen tapa toteuttaa tällainen
Workerobjekti on Runnable –rajapinnan käyttö, jonka toteuttava luokka sisältää
run()metodin joka puolestaan kutsuu Workermetodia. Workerobjektin suoritus
(execution) edelleen suorittaa Workermetodin.
Tätä suunnittelumallia voidaan käyttää sieppamaan operaatioita pointcutmäärittelyjen
kanssa. Voidaan myös automaattisesti generoida Workerobjekteja jotka kapseloivat
nuo operaatiot. Näitä objekteja voidaan välittää sopiville metodeille suoritettavaksi.
Tässä esiteltävässä mallissa käytetään aspektia luomaan automaattisesti anonyymien
workerluokkien objekteja. Nämä objektit ovat Workerobjekteja. Aspektiin kirjoitetaan
pointcut, joka sieppaa kaikki ne join pointkohdat, jotka on ohjattava Worker
objekteille. Seuraavaksi kirjoitetaan advice joka suorittaa nämä siepatut join point
kohdat anonyymien workerluokkien run()metodissa.
Tavallisesti around advice –lohkossa suoraan kutsuttu proceed() –metodi suorittaa
siepatut join point –kohdat. Tässä mallissa proceed() –metodia kutsuu around advice
joka on Runnablerajapinnan implementoivan anonyymin luokan sisältämässä run()
metodissa, ja saa Workerobjektin palautteena. Tämän objektin run()metodia voidaan
käyttää esimerkiksi säikeissä suorittamaan siepatut join point –kohdat.
23
Seuraavaa mallin kaavaa voi käyttää implementointiin. Ensin kirjoitetaan pointcut joka
sieppaa halutut join point kohdat. Koska kaavassa ei tarvita kontekstia, voidaan käyttää
nimettyä tai anonyymiä pointcutmäärittelyä. Seuraavaksi pointcut ohjeistetaan kirjoit
tamalla siihen around advice, jonka rungossa luodaan anonyymin luokan Worker
objekti. Se luodaan kutsumalla run() metodissa proceed() metodia sen sijaan,
että kutsuttaisiin jotain tiettyä metodia. Tämän jälkeen Workerbjektia voidaan käyttää
halutulla tavalla.
void around() : <pointcut> {
Runnable worker = new Runnable () {
public void run() {
proceed();
}}
... Lähetä workerobjekti johonkin jonoon suoritettavaksi,
... tai lähetä workerobjekti jollekin alijärjestelmälle
suoritettavaksi,
... tai kutsu pelkästään run() –metodia suoraan.
}
Esimerkki kaavan käyttämisestä [Lad03]:
Liitteessä 2 on uudelleen käytettävän abstraktin Asynchronous
ExecutionAspect aspektin lähdekoodi. Aspekti suorittaa erillisessä säikeessä
kaikki join point kohdat, jotka asyncOperations() –niminen pointcut määrittelee.
Aspekti sisältää abstraktin pointcutesittelyn, sekä around advice ohjeen sille.
Aspektin advice runko luo anonyymin luokan objektin, joka implentoi Runnable
rajapinnan. Run()metodi kutsuu proceed()metodia, joka suorittaa join point –kohdat.
worker –objekti suorittaa ohjeistetut joint point –kohtien operaatiot. Advice käyttää
sitä uuden säikeen luomiseen ja aspekti käynnistää luodun säikeen.
24
AsynchronousExecutionAspect aspektissa esitelty abstrakti pointcut määritel
lään konkreettisessa aliaspektissa.
Liitteen 2 SystemAsynchronousExecutionAspectaspekti sisältää tarvittavan
pointcut –määrittelyn. Aspektissa määritellään ohjelmallisesti varmuuskopion teko ja
nouto välimuistista tapahtumaan eri säikeissä. SystemAsynchronousExecu
tionAspect mahdollistaa CachePreFetcher ja Projectsaver luokkien
metodien asynkronisen suorituksen. Näiden luokkien ja Testluokan lähdekoodi on
liitteessä 2.
Nämä voidaan kääntää ajckomennolla, joka vastaa Javakielen javackomentoa, sekä
ajaa normaalisti javakomennolla:
> ajc CachePrefetcher.java ProjectSaver.java Test.javaAsynchronousExecutionAspect.java SystemAsynchronousExecutionAspect.java
> java Test
Tulosteena saadaan CachePrefetcher ja ProjectSaver –luokkien viestit ja säi
keiden nimet.
3.1.1 Workermetodin palauttama arvo
Edellisen kappaleen esimerkkin ohjatut kutsut voivat palauttaa arvon kutsujalle. Tällöin
proceed() palauttaa metodin arvon, kun operaatio on suoritettu loppuun. Tämän ar
von voi pitää Workerobjektissa tai palauttaa around advice –lohkosta. Jotta arvo olisi
järkevä, kutsujan pitää odottaa kunnes Workerobjektin suoritus on päättynyt. Edellä
kerrotussa asynkronisessa esimerkissä kutsuvan säikeen palauttama arvo ei vastaa ope
raation arvoa, koska säie palauttaa arvon välittömästi ja operaatio voidaan suorittaa
myöhemmin.
Paluuarvo voidaan ottaa talteen yleisellä tavalla, esimerkiksi kirjoittamalla abstrakti
luokka RunnableWithReturn, joka implementoi Runnable –rajapinnan. Runna
bleWithReturnluokkaa implementoivissa luokkissa run() –metodin pitää aset
25
taa _returnValuejäsen proceed()metodin palautusarvoksi, koska se on suorite
tun join point –kohdan palautusarvo. RunnableWithReturnluokan lähdekoodi on liit
teessä 2.
Tätä RunnableWithReturnluokkaa voidaan käyttää perusluokkana anonyymille
luokalle advicelohkossa, kuten liitteen 2 SynchronousExecutionAspect
aspektissa. Around advice –lohkon proceed()metodin paluuarvo on asetettu
_returnValuejäsenen arvoksi. proceed()metodin palauttama objekti on kääre
objekti (wrapper), joten paluuarvon tyypistä ei tarvitse huolehtia.
Workerobjektin luonti suunnittelumallia voi hyödyntää erilaisissa tilanteissa, kuten
auktorisoinnissa ja säieturvallisuudessa myös Swingkomponenttien kanssa. Worker
objektia voi joissakin tilanteissa käyttää heti luonnin jälkeen, koska se ilmentää silloin
kontekstia. Näin tehdään esimerkiksi tämän raportin transaktionhallinnan esimerkissä.
3.1.2 Workerobjektin kontekstin hallinta
Workerobjektin luonti –suunnittelumallin soveltamisessa voi olla tarpeellista koota
pointcutkohdan konteksti talteen muuta käyttöä varten. Muuttumattoman kontekstin
saa talteen ja välitettäväksi advicelohkossa proceed() –metodin avulla seuraavasti:
void around([context]) : <pointcut> {
Runnable worker = new Runnable() {
Proceed([context]);
}}
... voit käyttää workerobjektia
}
26
3.2 Madonreikä suunnittelumalli
Madonreikä suunnittelumalli (Wormhole pattern) välittää kutsujan informaation kon
tekstin kutsunvastaanottajan käytettäväksi ilman, että informaatiota tarvitsisi välittää
kontrollivirrassa (control flow) kullekin metodille parametrijoukkona. Esimerkiksi auk
torisoinnissa järjestelmän usean metodin on tiedettävä, kuka niitä kutsui, jotta ne voisi
vat päätellä onko kutsujalla lupa tähän operaatioon. Madonreikäsuunnittelumalli sallii
käyttää kutsuvaa objektia ja sen kontekstia tämän tiedon hankkimiseen.
Malli luo suoran reitin kahden tason välille kutsupinossa (call stack), jolloin vältytään
kaikkien tasojen läpikäymiseltä. Tämä säästää kutsuketjun modifioinnilta kun halutaan
välittää kontekstin informaatiota, se myös ehkäisee niin sanottua APIsaastetta.
Jos AspectJ ei ole käytössä, voi monisäikeisessä ympäristössä välittää kutsujan konteks
tin kahdella tavalla. Voit välittää kontekstin informaation lisäparametrien avulla, mikä
aiheuttaa API –saastetta, koska kaikille metodeille pitää määritellä ylimääräisiä para
metrejä. Toinen tapa on käyttää kutsujan luomia säiekohtaisia varastoja kontekstin aset
tamiseksi ja käyttämiseksi. Tässäkin tavassa useat moduulit ovat mukana informaatiota
välittävässä logiikassa.
3.2.1 Madonreikä suunnittelumallin periaate
Malli voidaan luoda määrittelemällä kaksi pointcutkohtaa: yksi kutsujalle ja yksi kut
sun vastaanottajalle. Kutsuja kokoaa kontekstin mikä välitetään tässä madonreiässä.
Madonreikä on määriteltävä vastaanottajan join pointkohtien suorituskohdista kutsujan
join pointkohtien kontrollivirtaan.
Kuva 4 esittää kutsupinoa tasoittain. Normaalisti konteksti voidaan siirtää taso kerral
laan, kunnes haluttu taso saavutetaan. Madonreikä mallissa tehdään polku suoraan ha
luttujen tasojen välille.
27
Kunkin tasonkonteksti
Kutsuja
Kutsuttu
Kutsupino
Madonreikä
Kuva 4. Madonreikämalli välittää kutsuttavan objektin kontekstin kutsupinon tasojen
ohi suoraan kutsujalle. Vaakatasot esittävät kutsun syvyyttä.
3.2.2 Madonreikämallin kaava
Mallin kirjoittamiseen voidaan käyttää seuraavaa kaavaa, jossa määritellään kutsuvan
tilan pointcut, joka kokoaa siihen liittyvän kontekstin. Samoin määritellään kutsun vas
taanottajan tilan pointcut. Koottu konteksti voi molemmissa tiloissa olla kohdeobjekti
tai muu parametri tarvittaville metodeille ja niiden suoritus.
Seuraavaksi luodaan pointcut joka toteuttaa madonreiän. Sekä kutsujan että kutsun vas
taanottajan pointcutlohkot sieppaavat omassa tilassaan join point –määrittelyistä kon
tekstinsa. pointcut wormhole lohkossa kutsun vastaanottajan calleeSpace()
pointcut määritellään samaan kontrollivirtaan kutsujan callerSpace() pointcut
kutsujen kanssa. Näin saadaan molempien join point –määrittelyjen konteksti käyttöön.
Tätä mallia käytetään tämän raportin transaktioesimerkissä myöhemmin.
28
Madonreikämallin kaava:
public aspect WormholeAspect {
pointcut callerSpace(<caller context>)
: <caller pointcut>;
pointcut calleeSpace(<callee context>)
: <callee pointcut>;
pointcut wormhole(<caller context>,
<callee context>)
: <cflow(callerSpace(<caller context>))
&& <calleeSpace(<callee context>);
// advices to wormhole
around (<caller context>, <callee context>)
: wormhole(<caller context>, <callee context>){
… advice body
}
}
Seuraava AccountTransactionAspect aspekti on yksinkertainen esimerkki tä
män mallin käytöstä. Aspekti luo madonreiän transaktion osapuolten kuten tilinhaltijan,
kassanhoitajan tai verkkopankin ja todellisten operaatioiden välille. AccountTransactio
nAspect.java:
29
public aspect AccountTransactionAspect {
// 1. transaktiojärjestelmän operaatioiden käyttö
pointcut transactionSystemUsage(TransactionSystem ts)
: execution(*TransactionSystem.*(..))&&this(ts);
// 2. tilioperaatioiden käyttö
pointcut accountTransactions (Account account,
float amount)
: this(account) && args(amount)
&& (execution(public * Account.credit(float))
|| (execution(public * Account.debit(float)));
// 3. luo madonreiän kohtien 1 ja 2 välille
pointcut wormhole (TransactionSystem ts,
Account account, float amount)
: cflow(transactionSystemUsage (ts))
&& accountTransactions (account, amount);
// 4. käyttää madonreiän mahdollistamaa kontekstia
before (TransactionSystem ts,
Account account, float amount) returning
: wormhole(ts, account, amount) {
// esim. kirjoitetaan loki transaktion operaatioiden
// etenemisestä, authorisoinnista jne.
}} // AccountTransactionAspect.java päättyy
30
Kohtien selitykset:
Kohta 1: transactionSystemUsage()poincut sieppaa kaikki suoritettavat join
point kohdat TransactionSystemobjektiin. Näin poincut voi kerätä transaktion
kontekstin ja säilyttää sen tässä objektissa.
Kohta 2: accountTransaction()pointcut sieppaa Account luokan credit()
ja debit() metodien suorituksen. Tämä pointcut säilyttää tiliä ja summaa koskevan
kontekstin.
Kohta 3: wormhole() pointcut luo madonreiän transaktiojärjestelmän operaatioiden
ja tililuokan operaatioiden välille sieppaamalla kaikki ne join point –määrittelyt kohdas
ta 2 jotka tapahtuvat transactionSystemUsage() kontrollivirrassa. Näin
wormhole() pointcut asettaa saataville kohdissa 1 ja 2 siepatun kontekstin.
Kohta 4: before advice voi nyt käyttää kontekstia, jonka wormhole()tarjoaa. Tä
mä advice voi tunnistaa tilin, summan, sekä tilinkäytöstä vastuussa olevan transaktiojär
jestelmän.
3.3 Poikkeuksien käsittely
AspectJkielinen advice ei voi heittää tarkistettua poikkeusta, elleivät sen join point –
kohdat voi heittää samaa poikkeusta. Tarkistettu poikkeus laajentaa suoraan tai epäsuo
rasti Exception –luokan, sen sijaan että laajentaisi RuntimeException –luokan.
Usein kuitenkin järjestelmän tehtävien koostaminen (weaving) vaatii, että aspekti käsit
telee tarkistettuja poikkeuksia. Esimerkiksi JDBC tietokantayhteyksissä tai JAAS auten
tikoinnissa aspektit voivat käyttää yleisiä ohjelmakirjastoja ja palveluja. Näistä heitetyt
poikkeukset ovat yleensä tarkistettuja poikkeuksia, joita alusta pystyy käsittelemään.
31
Lisäksi tietyn tyyppiset poikkileikkaukselliset tehtävät, kuten virheistä toipuminen ja
transaktionhallinta vaativat, että aspekti sieppaa kaikentyyppiset poikkeukset joita suori
tettavat joint point –kohdat heittävät. Näitä varten aspektin catchlohkoon on määritel
tävä poikkeuksen käsittelyn logiikka. Tilannetta hankaloittaa vielä se, että aspektit ovat
uudelleenkäytettäviä, eivätkä ne osaa käsitellä liiketoimintakohtaisia poikkeuksia.
Poikkeuksien käsittely malli perustuu siihen, että suorituslogiikassa syntyvä alkuperäi
nen, tarkistettu poikkeus poimitaan ja heitetään uusi ajonaikainen poikkeus, joka käärii
alkuperäisen poikkeuksen. Kun ajonaikainen poikkeus tapahtuu, siitä ilmoitetaan poik
keuksena ylemmän tason kutsujalle. Sen on myös voitava käsitellä poikkeus, vaikka se
ei olisi tietoinen järjestelmän aspektien määrittelyistä.
Mallin mukaisesti voi käsitellä liiketoimintakohtaisia poikkeuksia uudelleenkäytettävis
sä aspekteissa. Toisin sanoen on luotava tehtäväkohtainen (concernspecific) ajonaikai
nen poikkeus, joka voi asettaa tarkistetun poikkeuksen aiheuttajakseen. Asettamisen voi
tehdä joko välittämällä poimitun poikkeuksen ajonaikaisenpoikkeuksen konstruktorille
tai käyttämällä Exceptionluokan initCause() –metodia. Esimerkiksi implementoi
taessa pysyvyysaspektia JDBC:tä käyttäen, voi heittää PersistenceRuntimeExcep
tion poikkeuksen, joka käärii alkuperäisen tarkistetun SQLException poikkeuksen.
3.3.1 Tehtäväkohtaisen poikkeuksen käsittely aspektissa
Esimerkkinä liitteen 2A ConcernAspect –aspekti toteuttaa poikkeuksen käsittelykaa
van. Kaava esittää mallin, jossa tehtäväkohtainen ConcernCheckedException –
tyyppinen poikkeus siepataan ja kääritään ConcernRuntimeException –tyyppiseksi
poikkeukseksi. Tämä säilyttää alkuperäisen poikkeuksen niin, että sen vastaanottaja voi
nähdä alkuperäisen poikkeuksen tyypin. Mallia käytettäessä sen käsitteiden nimet kor
vataan tehtäväkohtaisilla käsitteillä.
Aspektissa käytetään ConcernRuntimeExceptionluokkaa, jonka konstruktori
saa alkuperäisen heitetyn poikkeuksen parametrikseen. Tästä parametristä ilmenee
32
poikkeuksen syy, mikäli kutsuja tarvitsee sitä. Aspektin before advice sieppaa alkupe
räisen tarkistetun poikkeuksen, käärii sen tarkistamattomaksi poikkeukseksi ja heittää
sen. Tässä advice saa alkuperäisen poikkeuksen void concernLogic() –metodilta.
ConcernAspectaspektin ja mallin muu lähdekoodi on testausluokkineen liitteessä 2A.
3.3.2 Liiketoimintokohtaisen poikkeuksen käsittely aspektissa
Samaa poikkeuksen esittely –mallia voi käyttää tehtäväkohtaisten tarkistettujen poikke
usten lisäksi myös liiketoimintokohtaisten tarkistettujen poikkeusten käsittelyyn. Jois
sain tilanteissa jonkin aspektin advicelohkon on kyettävä poimimaan kaikki poikkeuk
set. Tällöin aspektin pitää pystyä heittämään edelleen liiketoimintokohtaisia poikkeuk
sia eli kun poikkeus on siepattu, sitä pitää pystyä käsittelemään jollain tavoin.
Edellä esitettyä ConcernAspect –aspektia voidaan muuttaa niin, että se ottaa kiinni
kaikki poikkeukset, jotka sen pointcut sieppaa. Lisätään aspektiin around advice, joka
käsittelee siepatut operaatiot try/catchlohkossa. Kun catchlohko on suoritetaan, se
muodostaa tehtäväkohtaisen virheenkäsittelyn ja heittää kutsujalle uuden Concern
RuntimeException –tyyppisen poikkeuksen, joka on kääre alkuperäiselle tarkistetulle
poikkeukselle. Muutettu ConcernAspectaspektin ja mallin muu lähdekoodi on testaus
luokkineen liitteessä 2B.
3.3.3 Poikkeuksen taltiointi aspektissa
Liiketoimintometodien suoritukseen tarvitaan liiketoimintakohtaisten poikkeusten käsit
telyä. Kun kaikki poikkeukset siepataan ja kääritään ajonaikaisiksi poikkeuksiksi, kut
sujan on saatava käyttöönsä poikkeuksen todellinen syy, jotta sitä voidaan käyttää to
teutettavan sovelluksen tarpeiden mukaan. Siepatusta poikkeuksesta syy voidaan ottaa
talteen ja lähettää se edelleen kutsujalle.
33
Edellisen kappaleen esimerkkiin voidaan lisätä PreserveBusinesException –
aspekti, joka ottaa talteen ajonaikaisen poikkeuksen syyn. Tämä aspekti ei ole uudel
leenkäytettävä vaan se on toteutettava järjestelmäkohtaisesti. Aspektin lähdekoodi on
käyttöesimerkkeineen liitteessä 2C.
PreserveBusinessExceptionaspektin toiminta: kun jokin metodi joka pystyy
heittämään liiketoimintakohtaisen poikkeuksen on heittänyt ConcernRuntimeEx
ceptiontyyppisen poikkeuksen, kutsutaan PreserveBusinessException
aspektissa after advice, joka voi myös heittää liiketoimintakohtaisen poikkeuksen. Ad
vicemäärittelyn rungossa tarkistetaan onko ajonaikaisen poikkeuksen aiheuttaja sellai
nen liiketoimintapoikkeus joka pitäisi säilyttää. Jos on, sen tyyppi pakotetaan Bu
sinessException –tyypiksi ja poikkeuksen syy heitetään edelleen kutsujal
le.PreserveBusinessExceptionaspektin lähdekoodi on liitteessä 2 C.
3.4 Osallistujasuunnittelumalli
Monilla järjestelmän operaatioilla on yhteisiä erityispiirteitä, kuten yksinkertaiset trans
aktioominaisuudet, methodin kutsujen ajallinen kesto, IOtoiminnon ominaisuudet,
etäkäytön ominaisuudet, tai muu ominaisuus jonka perusteella jotkin operaatiot ovat
osallisena johonkin järjestelmään laajalti vaikuttavaan tehtävään. Koska nämä operaati
ot ovat hajotettuna useissa moduuleissa, operaatioiden käytöksen laajentaminen ja
muuttaminen on poikkileikkauksellinen tehtävä. Osallistujasuunnittelumalli tarjoaa
erään tavan modularisoida tällaista erityistuntomerkkeihin pohjautuvaa poikkileikkauk
sellista tehtävää. Malli auttaa sieppaamaan join point –kohdat erityispiirteiden perusteel
la silloin kun nimeen perustuva sieppaus ei riitä.
Kun halutaan hallita järjestelmän hitaita operaatioita ja niiden aiheuttamaa odotusaikaa,
voidaan näitä operaatioita suorittavia metodeja hakea metodien nimeen (signature) pe
rustuvilla pointcut –määrittelyillä. Esimerkiksi kun metodi heittää IOException –
poikkeuksen, voidaan päätellä että se suorittaa hitaan IOoperaation. Näin voidaan tun
34
nistaa joitain hitaita metodeja. Myös metodin nimestä voidaan päätellä sen ominaisuuk
sia. Esimerkiksi kun nimi on set alkuinen, kyseessä on todennäköisesti tilaan vaikut
tava operaatio. Näin ei kuitenkaan voida määritellä kaikkien operaatioiden erityistunto
merkkejä. Operaatiohan voi olla esimerkiksi hidas, koska se suorittaa monimutkaisia
laskutoimituksia, eikä metodin nimi paljasta sen ominaisuuksia. Siksi ominaisuuksiin
perustuvaa pointcutmäärittelyä ei voida koostaa reflektoimalla jokerimerkeillä (wild
cards).
Jotta tällaiset erityispiirteitä omaavat join point –kohdat voidaan siepata pointcut
määrittelyyn, tarvitaan niitä toteutettavien luokkien yhteistyötä. Eräs mahdollisuus on
täydentää implementointia metadatalla, joka ilmaisee erityispiirteet silloin kun se ei ole
johdettavissa nimeämiskäytännöstä. Tätä ei kuitenkaan voida käyttää vielä AspectJ
kielisessä toteutuksessa.
AspectJtoteutuksessa Osallistujasuunnittelumalli (The Participant pattern) auttaa
sieppaamaan metodit niiden erityispiirteiden perusteella. Malli kuitenkin vaatii ydinto
teutuksen modifiointia, joten on mahdollista että osa muutoksia tarvitsevista operaati
oista jää tunnistamatta. Siksi usein on turvallisempaa käyttää vakiintuneita nimeen tai
ominaisuuteen perustuvia pointcutmäärittelyjä.
Osallistujasuunnittelumallissa ohjelmaluokkien sisältämä poincut viittaa edelleen joi
hinkin erityispiirteisiin. Sen sijaan että poincutmääritelmä sisällytettäisiin kuhunkin
luokkaan ja käytettäisiin aspektien advicelohkossa, luokkiin itseensä määritellään ali
aspekti (subaspect). Tämä aliaspekti laajentaa ohjeistavaa aspektia (advising aspect) ja
sisältää pointcutmääritelmän. Tavallaan tämä määritelmä kääntää aspektin ja luokan
roolit päinvastaisiksi. Sen sijaan että tehtäisiin aspektit tietoisiksi luokista ja metodeista,
nyt tehdään luokat tietoisiksi aspekteista.
Malli rakentuu seuraavasti. Ensin kirjoitetaan abstrakti aspekti, joka sisältää yhden tai
useampia abstrakteja pointcutmäärittelyjä, jotka viittaavat haluttuja erityispiirteitä
omaaviin join point –kohtiin. Nämä pointcutmäärittelyt muodostavat eräänlaisen ’as
pektuaalisen rajapinnan’. Aspektin advicelohkossa jokaista pointcutmäärittelyä –tai
35
niiden kompinaatioita ohjeistetaan käyttäytymään halutulla tavalla. Tätä aspektia
voidaan pitää kutsuvana aspektina (inviting aspect), jonka kutsuu muita osallistumaan
advicelohkossaan. Kutsu voi olla pelkkä yksittäinen kutsu, tai se voi sisältää valinnaisia
ehtoja.
Jokaisen luokan, jota halutaan käyttää osallistujana, pitää sisältää konkreettinen alias
pekti, joka laajentaa kutsuvaa aspektia. Tämä aliaspekti toteuttaa luokassaan abstraktin
pointcutmäärittelyn konkreettisesti. On huomattava että konkreettisen aliaspektin ei
tarvitse olla luokan sisäaspekti, vaan se voi olla esimerkiksi vertaisaspekti (peer aspect).
Aliaspekti on määriteltävä jokaiseen luokkaan, jota halutaan käyttää osallistujana yh
teistyössä. Kuva 5 esittää Osallistuja suunnittelumallin tyypillistä rakennetta UML
kaaviona.
<<aspect>>AbstractDesiredCharacteristicAspect
+pointcut desiredCharacteristicJoinPoints()
<<aspect>>MyClass1.DesiredCharacteristicParticipant
+pointcut desiredCharacteristicJointPoints()
<<aspect>>MyClass2.DesiredCharacteristicParticipant
+pointcut desiredCharacteristicJoinPoints()
MyClass1
MyClass2
sieppaa piirteet
sieppaa piirteet
Kuva 5. Osallistujasuunnittelumallin esimerkki UML notaationa
36
3.4.1 Osallistujasuunnittelumallin kaava
Seuraava mallin kaava (template) sisältää abstraktin aspektin sekä kaksi luokkaa, joissa
on sisäaspekti. Näissä sisäaspekteissa toteutetaan abstrakti pointcutmäärittely konk
reettisesti.
AbstractDesiredCharcteristicAspect.java perustaa yhteistyön. Mallissa on käytetty
around advice määrittelyä, yhtä hyvin voi olla myös before tai after advice.
abstract aspect AbstractDesiredCharcteristicAspect {
public abstract pointcut desiredCharacteristicJoinPoints();
Object around() : desiredCharacteristicJoinPoints() {
// tähän advice koodi
}
}
Haluttu poikkileikkauksellinen käytös kirjoitetaan around advice lohkossa, joka määrit
telelee abstraktin pointcut esittelyn.
MyClass1.java on osapuolena yhteistyössä. Luokkaan pitää määritellä sisäaspekti, joka
on AbstractDesiredCharcteristicAspectperusaspektin aliaspekti. Abstrakti
desiredCharacteristicJoinPoints pointcut toteutetaan tässä MyClass
luokassa. Aliaspekti julistaa, että tämän luokan metodeilla haluttuja piirteitä, joten ne
voivat olla osapuolena perusaspektin tarjoamassa yhteistyössä.
public class MyClass1 {
// MyClass1luokan koodi
public static aspect DesiredCharacteristicParticipant
extends AbstractDesiredCharcteristicAspect {
public pointcut desiredCharacteristicJoinPoints() :
call(* MyClass1. desiredCharacteristicMethod1())
|| call(* MyClass1. desiredCharacteristicMethod2())
37
/* || jne...*/;
}}
Samaan tapaan voidaan määritellä muita yhteistyöhön osallistuvia luokkia. Osallistuja
mallissa luokat voidaan täsmällisesti määritellä poikkileikkauksellisen tehtävän toteutta
jiksi yhteistyön osapuolina. Samoin on mahdollista määritellä osapuoleksi luokka
hierarkia tai pakkaus. Silloin aliaspekti on määritelty luokan ulkopuolelle, ja tässä osa
puolen aliaspektissa on pointcutlohkossa ylläpidettävä listaa tarvittavista halutunpiirtei
sistä metodeista. Listan toteutus voi perustua myös metodien nimen mukaiseen reflek
tiiviseen valintaan.
38
4 SÄIETURVALLISUUS
Säieturvallisuus vaatii järjestelmän oikean käyttäytymisen ylläpitoa tilanteessa, jolloin
järjestelmässä useat säikeet käyttävät sen tilaa. Koska säikeet vaikuttavat eri osissa so
vellusta, niiden toteutus, muuttaminen ja testaus voi aiheuttaa ongelmia. Mikäli tässä ei
onnistuta, järjestelmässä esiintyy käyttöliittymätasolla epämääräistä tulostumista, datan
eheys on puutteellista ja järjestelmä voi hidastella tai lukittua kokonaan.
Näihin ongelmiin on käytettävissä ratkaisumalleja. Niissä määritellään objektien luki
tuksia jotta vältyttäisiin järjestelmän lukkiutumiselta ja pyritään tehokkaaseen mo
niajoon sekä välttämään ylikuormitus. Jokainen ongelma on kuitenkin analysoitava
erikseen, jotta voidaan valita oikea ratkaisumalli.
Tässä raportissa esitetään kaksi AspectJkielistä ratkaisua. Swingtoteutuksen mallissa
pyynnöt välitetään suojatuille objekteille määrätyssä säikeessä. Toisessa mallissa käyte
tään lukemisen ja kirjoittamisen lukitusta (readwrite lock pattern).
4.1 Swing ja yhden säikeen sääntö
Swing, joka on hyvin yleinen Javan GUI kirjasto, käyttää yhden säikeen turvallisuus
sääntöä. Se vaatii, että kaikkien Swingkomponenttien on käytettävä vain yhtä tapahtu
masäiettä (eventdispatching thread). Rajoittamalla käytön vain yhteen määrättyyn säi
keeseen, malli siirtää turvallisuus ongelmat pois komponentin toteutuksesta. Kun toinen
säie tarvitsee Swingkomponentin palveluja, sen pitää pyytää tapahtumasäiettä suorit
tamaan operaatio, koska ainoastaan tapahtumasäie saa päivittää näkyviä komponentteja.
Yksinkertaisissa sovelluksissa, joissa ei ole käyttäjän luomia säikeitä, tämä sääntö toi
mii hyvin. Monimutkaisissa sovelluksissa, joissa taustasäikeet (noneventdispatching
thread) käyttävät UIkomponentteja, sääntö on kuitenkin rajoittava tekijä. Esimerkiksi
tilanne, jossa tapahtumasäie suorittaa verkkoon tai IOkeskeytyksiin liittyviä operaatioi
ta tai tietokantatoimintoja, ja taustasäikeen pitäisi pystyä päivittämään käyttöliittymää
palvelimen informaatiolla. Kun palvelimelle lähetetään pyyntö, jonka perusteella käyt
39
töliittymä päivitetään, tapahtumasäie ei voi jäädä odottamaan vastausta, koska tällöin
koko käyttöliittymä lukittuu kunnes palvelin vastaa. Jotta näin ei kävisi, jonkun toisen
säikeen on odotettava, jotta käyttöliittymää voidaan päivittää. Toisaalta päivitystä ei voi
jättää taustasäikeen tehtäväksi, koska näyttö jää epämääräiseen tilaan.
Ratkaisuksi tähän ongelmaan Swing sallii tapahtumasäikeen käyttämisen muiden säi
keiden pyyntöjen välittämiseen. Säikeiltä voi jättää operaation suorituspyyntöjä tapah
tumasäikeelle käyttämällä EventQueue.invokeLater() tai Event
Queue.invokeAndWait()kutsuja. Molemmissa välitetään Runnable –objekti, jon
ka run() –metodi suorittaa aiotun operaation. Tässä ratkaisun hankaluudeksi tulee
Runnable –luokkan laajennusluokkien kirjoittaminen jokaista eiAWTsäikeestä kut
suttavaa metodia varten, sekä näiden luokkien käyttäminen suorien kutsujen sijaan. On
myös varmistettava luokan on yhdessä EventQueue luokan kanssa korvattava kaikki
eiAWTsäikeestä tulevat kutsut.
4.1.1 Perinteinen ratkaisu
Liitteessä 3 Testluokan lähdekoodi esittää säieturvallisuuden perinteisen ratkaisun.
Testluokka toteuttaa käyttöliittymän ikkunan (frame). Tällöin yhden säikeen sääntö
astuu voimaan.
Tämän jälkeen tehdyt metodin kutsut kääritään anonyymeihin Runnable –luokan il
mentymiin. Tämän luokan run() –metodi kutsuu suoritettavia metodeja. Anonyymin
luokkan sijaan voisi yhtä hyvin käyttää nimettyjä luokkia. Ensimmäinen metodi tulostaa
ikkunaan pienen taulukon. Metodi suoritetaan asynkronisesti, joten sille kutsutaan
EventQueue.invokeLater()metodia. Toinen metodi tulostaa OK
viestipainikkeen. Tämä metodi suoritetaan asynkronisesti, joten sille kutsutaan Event
Queue.invokeAndWait()metodia.
Seuraavaksi välitetään reititysluokan (Runnable) ilmentymä. invokeLater()
metodia käytetään jos kutsu pitää suorittaa jumiuttamatta kutsujaa. invokeAnd
40
Wait()metodia käytetään silloin, kun kutsujan pitää jäädä odottamaan operaation
suoritusta.
Testluokan kohtien selitykset:
Kohta 1: Tällä kohtaa käyttöliittymä toteutetaan eli kehys näkyy kuvaruudulla. Taus
tasäikeitä voi kutsua vielä ennen tätä kohtaa, koska AWTsäie ei käytä komponentteja.
Pääsäie on ainoa säie, joka päivittää käyttöliittymäkomponentin tilaa.
Kun käyttöliittymä on toteutettu, AWTsäie lukee ja päivittää sovelluksen tilaa ja koska
Swingluokat eivät tarjoa mitään poissulkevaa käytön suojausta, on pyydettävä tapah
tumasäiettä suorittamaan operaatio kutsuvan säikeen puolesta. Näin taataan, että vain
tapahtumasäie hallitsee käyttöliittymän komponentteja.
Kohta 2: Pyyntö taulukon kentän arvon asettamisesta voidaan tehdä asynkronisesti,
koska ei ole vaatimusta siitä milloin tämä operaatio on suoritettu loppuun. Ensin pyyntö
kääritään Runnableluokan toteuttavaan anonyymiin luokkaan, jonka run() –
metodissa alkuperäinen operaatio jatkuu. Tässä joudutaan antamaan paikallisille muut
tujille final –määre, koska paikallissa luokissa käytetyille muuttujille Java pakottaa
antamaan tämän määreen.
Koska operaatioille ei ole vaadittu synkronista suoritusta, käytetään Event
Queue.invokeLater() metodia lähettämään pyyntö operaatioiden suorituksesta
asynkronisesti. Nämä pyynnöt menevät tapahtumankäsittelyjonoon, josta tapahtumasäie
poimii pyyntöobjektit ja kutsuu run()metodia, joka suorittaa operaatiot.
Kohta 3: JoptionPane.showMessageDialog()metodin kutsu suoritetaan synk
ronisesti kutsujan kanssa. Kutsuja ei voi edetä seuraavaan operaatioon ennenkuin kutsu
on suoritettu, koska kyseessä on toimenpide, joka odottaa ilmoituksen kuittaamista.
Mikäli operaation pitää jäädä odottamaan, käytetään Event
Queue.invokeAndWait()metodia pyynnön lähettämiseen. Pyyntö laitetaan tapah
tumankäsittelyjonoon ja kutsuja jää odottamaan pyyntöobjektin suorittamista, kun se on
41
suoritettu kutsuja vapautetaan. Tämän Testluokan suorituksessa käyttäjä joutuu pai
namaan OKnappulaa, ennenkuin suoritus jatkuu.
Kohta 4: Kun getRowCount()metodin kutsulta odotetaan paluuarvoa, operaation
pitää toimia synkronisesti kutsujan kanssa, koska seuraava operaatio voi riippua paluu
arvosta. Koska paikallisissa luokissa käytetyt muuttujat ovat finalmääreisiä, ei get
RowCount()metodista saatua paluuarvoa voida sijoittaa rowCountmuuttujaan.
Paluuarvo otetaan talteen luomalla uusi finalmääreinen integertaulukko, sijoitetaan
paluuarvo taulukon elementiksi ja lopulta asetetaan tämä elementti rowCount
muuttujan arvoksi. Jos Testluokassa olisi käytetty nimettyjä luokkia, olisi niihin voi
nut lisätä jäseniä paluuarvoa varten.
Kohta 5: Kutsu getGridColor()metodille on yhtäläinen getRowCount()
metodin kanssa, paitsi että paluuarvo saadaan objektina.
Poikkeuksia ei ole käsitelty Testluokassa, jotta esimerkistä ei tulisi pitkä. Liitteessä 3
on LogUIAspectaspekti lähdekoodi, jolla voi kirjoittaa lokin Testluokan toimin
nasta.
LogUIAspectaspekti kirjoittaa lokin kaikista metodien kutsuista luokissa, jotka ovat
javax –pakkauksessa ja sen alipakkauksissa. Aspektin before advice kirjoittaa metodin
sijainnin ja nimen (signaturen) ja Thread.currentThread()metodia käyttävän
kutsuvan säikeen nimen.
4.1.2 Säieturvallisuus aspektina
Tässä kappaleessa esitetään AspectJkielinen säieturvallisuuden ratkaisu, joka noudattaa
yhden säikeen sääntöä. Ratkaisussa sovelletaan Workerobjektin luonti –
suunnittelumallia. Käyttöliittymää päivittävät metodit siepataan ja suunnittelumallin
mukaisesti luodaan Runnableobjekti. Tarkistetaan onko kutsuva säie jo tapahtuma
jonossa. Jos se on jonossa, annetaan alkuperäisen metodin suorittaa operaatio, jos se ei
ole jonossa Workerobjekti laitetaan tapahtumajonoon odottamaan suoritusta.
42
Tässä aspektiratkaisussa toteutetaan Swingin yhdensäikeen sääntö, jolloin säikeiden
käsittelyä ei tarvitse toteuttaa Testluokassa, kuten perinteisessä ratkaisussa tehtiin.
Aspekti reitittää tapahtumasäikeen kautta ne metodien kutsut, jotka käyttävät tai muut
tavat Swingkomponenttien tilaa. Tämän aspektin yhteydessä edellä esitettyä LogUIAc
tivitesAspectaspektia voi käyttää UIoperaatioiden lokin kirjoittamiseen.
Ratkaisussa esitetään abstrakti aspekti, josta implementoidaan konkreettinen aliaspekti,
joten ratkaisua voi soveltaa erilaisiin järjestelmiin aliaspektia muuttamalla.
Liitteessä 4 on esitetty abstraktin SwingThreadSafetyAspectaspektin, konk
reettisen DefaultSwingThreadSafetyAspectaliaspektin sekä ratkaisua testaa
van Testluokan lähdekoodi.
SwingThreadSafetyAspectaspektin abstrakti uiMethodCallspointcut siep
paa käyttöliittymän metodien kutsut. Pointcut määritellään konkreettisessa DefaultS
wingThreadSafetyAspectaliaspektissa.
SwingThreadSafetyAspectaspektin abstrakti uiSyncMethodsCalls
pointcut sieppaa synkronista suoritusta tarvitsevat metodit. Pointcut määritellään konk
reettisessa DefaultSwingThreadSafetyAspect aliaspektissa.
SwingThreadSafetyAspectaspektin threadSafeCallspointcut luettelee
ne metodien kutsut, joiden ei tarvitse noudattaa yhden säikeen sääntöä. Näitä kutsuja
ovat JComponent.revalidate(), JComponent.repaint() sekä kuunteli
joiden lisääminen ja poistaminen.
SwingThreadSafetyAspectaspektin excludedJoinpoints pointcut
lohkossa valitaan metodien kutsut. Lohkossa poissuljetaan threadSafeCalls()
poincut lohkon sieppaamat aspektin join pointkohdat sekä ne jotka suoritetaan tapah
tumasäikeessä. Ehto if(EventQueue.isDispatchhThread()) saa totuusar
von true, vain jos tämänhetkinen suoritettava säie on tapahtumasäie.
43
SwingThreadSafetyAspectaspektin routedMethods() poincut sieppaa ne
join point –kohdat, jotka tarvitsevat kutsujen reittämistä tapahtumasäikeen kautta.
SwingThreadSafetyAspectaspektin around advice –lohkoissa varmistetaan
reititystä tarvitsevien metodien suoritus Workerobjektina synkronisesti. Myös void
metodeille, jotka uiSyncMethodsCallspointcut määrittelee, varmistetaan synkroni
nen suoritus.
Liitteessä 4 esitetään myös DefaultSwingThreadSafetyAspectaliaspekti,
jonka avulla SwingThreadSafetyAspectaspekti kontrolloi metodien reititystä.
Aliaspektissa määritellään synkronista reititystä vaativat metodit.
Aliaspektin pointcutmäärittelyjen selitys:
viewMethodCalls() –pointcut valitsee kutsut UIkomponenttien metodeille. Se
sieppaa kaikki JComponentluokan ja sen aliluokkien metodit.
modelMethodCalls() –pointcut valitsee kaikki UImalliluokkien ja niiden aliluok
kien metodeille.
uiMethodCalls() –pointcut valitsee kutsut UImetodeille yhdistettynä.
uiSyncMethodCalls() pointcut valitsee kaikki kutsut JOptionPaneluokan ja sen
aliluokkien metodeille. Ne ovat synkronista suoritusta vaativia UImetodeja.
4.1.3 Objektin lukitusmalli aspektina
Kilpailevien säikeiden luku ja kirjoitusoperaatioiden kohdistuessa samaan objektiin
voidaan sen eheys varmistaa lukintamenettelyllä. Menettelyn perusajatuksena on, että
objektia voi lukea, ellei sen tilaa olla parhaillaan muuttamassa. Perinteisessä luokkato
teuksessa lukitusmekanismi vaatii jokaisen luku ja kirjoitusmetodin kohdalla erillistä
koodausta. Seuraavassa esitetään lukitusmalli, johon kuuluu uudelleenkäytettävä perus
44
aspekti sekä sen aliaspekti, jota muokkaamalla mallia voidaan käyttää eri sovellusten
luokkien kanssa. Näiden aspektien ja esimerkkiluokkien lähdekoodi on liitteessä 5.
Perusaspekti kapseloi lukitusmekanismin mallin. Aspekti sisältää kaksi pointcut määri
telyä, ensimmäinen sieppaa lukumetodien suoritukset ja toinen kirjoitusmetodien. Nämä
suoritukset voidaan synkronoida.
Liitteen 5 ReadWriteLockSyncronizationAspectaspekti käyttää JSR166
luokkakirjastoa, joka on noudettavissa verkosta, katso [Lea05]. Luokkakirjasto on kehit
teillä ja täysin valmistuttuaan se tulee olemaan käytössä Javan ja
va.util.concurrent –pakkauksena.
ReadWriteLockSyncronizationAspectaspektin selitys kohdittain:
perthis()assosioi aspektin instanssit niihin Workerobjekteihin, jotka sopivat luku
tai kirjoitusmetodeihin. Nämä on metodit luetellaan konkreettisessa aspektissa. Uusi
aspektin instanssi luodaan jokaiselle objektille, jolle siepattu metodi suoritetaan. Asso
siaatio mahdollistaa lukitusobjektin esittelyn kaikille synkronoiduille luokille, objektin
tyyppiä ei tarvitse tietää.
Abstrakti readOperations() –pointcut määritellään aliaspektissa sieppaamaan
kaikki ne metodit, jotka eivät muuta objektin tilaa. Aliaspektissa määritellään myös
abstrakti writeOperations()pointcut sieppaamaan metodit, jotka muuttavat objek
tin tilaa.
Lukitusmallissa _lock –muuttuja palvelee syknronoinnin tukena. Koska aspekti on
assosioitu tarvittavien join point –kohtien objekteille, _lock –muuttuja on assosioitu
objektien instansseihin.
before():readOperations() ja after():readOperations() advice loh
koissa lukitaan ja vapautetaan lukuoperaatiot.
Samoin before:writeOperations() ja after:writeOperations()
advice lohkoissa lukitaan ja vapautetaan kirjoitusoperaatiot.
45
Poikkeusten pehmentäminen konvertoi acquiremetodien kutsujen heittämät Interrup
tedException –poikkeukset, joten siepattujen operaatioiden API:a ei tarvitse muuttaa.
ReadWriteLockSyncronizationAspectaspektia voidaan käyttää aliaspektin
kanssa yksittäiselle luokalle tai koko luokkakirjaston lukitusmallina. Esimerkinä Ban
kingSynchronizationAspectaspekti mahdollistaa mallin soveltamisen Ac
countluokkaan, myös näiden lähdekoodi on liitteessä 5.
46
5 AUTENTIKOINTI JA AUKTORISOINTI
Autentikointi (authentication) on oikeuksien tarkistamisprosessi, jossa todennetaan
käyttäjän tunnisteet, esimerkiksi käyttäjätunnuksen ja salasanan avulla. Auktorisointi
(authorization) on prosessi, jossa tarkistetaan autentikoidun käyttäjän oikeudet käyttää
tarjolla olevia resursseja. Näiden prosessien toteuttamiseksi rakennetut API:t, kuten
Java Authentication and Authorization Service (JAAS), mahdollistavat resurssien käytön
valvonnan erottamisen muusta koodista. Näiden ohjelmarajapintojen rinnalle on kehitet
ty erilaisia standardeja konfiguraation määrittelykieliä, kuten Security Assertion Mar
kup Language (SAML) ja Extensible Access Control Markup Language (XACML).
Näiden API:en ja standardien pyrkimyksenä on ollut vähentää monimutkaisuutta ja tar
jota nopeita ja toimivia toteutuksia.
Vaikka näitä ohjelmointirajapintoja käytettäisiin, perinteiset ohjelmointitavat vaativat
moduulikohtaista autentikoinnin ja auktorisoinnin koodaamista. Esimerkiksi liiketoi
mintasovelluksen käyttäjien oikeuksien valvonnan toteuttamiseksi pitää lisätä kaikkiin
liiketoimintametodeihin kutsut JAASmetodeille. Sama koskee myös käytön valvonnan
logiikkaa, koska liiketoimintalogiikka levittäytyy useisiin moduleihin,.
Seuraavassa kappaleessa esitellään yksinkertainen pankkijärjestelmä. Tähän järjestel
mään lisätään myöhemmin autentikointi ja auktorisointi AspectJratkaisuna.
5.1 Pankkijärjestelmä
Pankkijärjestelmän ydintoteutus sisältää seuraavat luokat: pankkitilin rajapintaluokka
Account.java, josta toteutetaan tililuokka AccountSimplImpl.java, poikkeuksia
käsittelevä luokka InsufficientBalanceException.java, tilinsiirron toteuttava
luokka InterAccountTransferSystem.java, sekä järjestelmän testaamiseksi
Test.java –luokka. Katso kuva 6.
47
Kuva 6. Pankkijärjestelmän ydintoteutus.
Accountrajapinta esittelee getmetodin tilinumeron saantiin, tilin hyvitys ja veloitus
metodin, sekä getmetodin tilinsaldolle. AccountSimplImplluokka toteuttaa tämän
rajapinnan. InterAccountTransferSystem –luokka sisältää tilinsiirtometodin.
Kaksi viimemainittua luokkaa voivat heittää InsufficientBalanceException
–poikkeuksen, mikäli tilin saldo ylittyy.
Ohjelman metodien suoritusjärjestyksen seuraamiseksi käytetään AuthLog
ging.java –aspektia, joka listaa tili ja tilinsiirtoluokan metodien nimet suoritusjär
jestyksessä.
Testluokassa luodaan kaksi tiliä, ensimmäistä tiliä hyvitetään kerran ja velotaan ker
ran, sekä suoritetaan kaksi siirtoa toiselle tilille, jolloin ensimmäisen tilin saldo ylittyy
ja ohjelma heittää poikkeuksen.
48
5.2 JAASpohjainen autentikointi perinteisellä tavalla
Järjestelmään voidaan lisätä autentikointitoiminnallisuutta, esimerkiksi autentikointi
tarvittaessa (justintime). Silloin autentikointi tapahtuu vasta kun käyttäjä yrittää avata
sellaista järjestelmän toimintoa, joka vaatii käyttäjän identiteetin todentamisen. Pelkässä
sisäänkirjautumisen autentikoinnissa (upfront login) kysytään käyttäjänimi ja salasana
ohjelman alussa, koska molemmissa autentikoinneissa periaatteessa kyse on samasta
asiasta. Seuraavissa kappaleissa kerrotaan (Java Authentication and Authorization Ser
vice) JAASpohjaisen autentikoinnin keskeisistä asioista, joita ovat:
Kirjautumisen kontekstin säilyttävän LoginContext –objektin luominen.
Takaisinkutsujen käsittelijät (callback handlers), jotka esittävät kirjautumis
pyynnön käyttäjälle.
Kirjautumisen konfiguraatiotiedosto (login configuration file) asettaa luokan, jo
ta käytetään autentikointimodulina. Näin konfiguraatiota voidaan muuttaa ilman
lähdekoodin muuttamista.
5.2.1 LoginContext –objektin luonti
Kirjautumisen kontekstin omaava objekti tarvitsee parametreikseen konfiguraation ni
men ja takaisinkutsun käsittelijän. Konfiguraation nimi on määritelty konfiguraatiotie
dostossa. Objektin luonti noudattaa kieliopillisesti seuraavaa kaava:
import javax.security.auth.login.*;
. . .
LoginContext lc =
new LoginContext(<config file entry name>,
<CallbackHandler to be used for user interaction>);
Pankkijärjestelmän Testluokassa se on toteutettu seuraavasti:
import javax.security.auth.login.*;
49
. . .
LoginContext lc =
new LoginContext("Sample",
new TextCallbackHandler());
lc.login();
5.2.2 Takaisinkutsun käsittelijä
Takaisinkutsun käsittelijä tarjoaa mekanismin autentikointitietojen saamiseksi käyttäjäl
tä. Se kysyy käyttäjältä nimen ja salasanan joko näytöllä keskusteluikkunassa tai ohjel
mallisesti. Käyttäjä voi olla ihminen tai toinen järjestelmän osa. Esimerkiksi Text
CallbackHandlerluokka, joka sisältyy Sunin JRE 1.4 –pakkaukseen, toimittaa au
tentikointiinformaation kutsuvalle järjestelmälle.
5.2.3 Kirjautumisen konfiguraatiotiedosto
Kirjautumisen konfiguraatiotiedosto asettaa luokan, jota käytetään autentikointi modu
lina. Kirjautumisen konfiguraatiotiedoston rakenne ja sisältö kieliopillisesti kuvattuna
on seuraavanlainen:
<name used by application to refer to this entry> {
<LoginModule> <flag> <LoginModule options>;
<optional additional LoginModules, flags and options>;};
Pankkijärjestelmän esimerkin sample_jaas.config –tiedosto assosioi tämän konfi
guraation samplepakkauksen SampleLoginModule.java – luokkaan. Optiolla de
bug=true saa tulostettua viestejä käyttäjälle kirjautumisen onnistuttua.
Pankkijärjestelmän sample_jaas.config konfiguraatiotiedosto:
50
Sample {
sample.module.SampleLoginModule required debug=true;
};
SampleLoginModule.java kysyy käyttäjä tunnuksen, joka on "testUser" ja
salasanan "testPassword", jotka annettuaan käyttäjä saa ilmoituksen kirjautumisen
onnistumisesta.
Pankkijärjestelmän autentikointia ja kirjautumisen konfiguraatiota varten samp
le_jaas.config –tiedosto sijoitetaan johonkin luokkapolussa olevaan hakemistoon,
kuten myös tutoriaalin sample niminen hakemisto, jonka modulealihakemisto sisäl
tää SampleLoginModule.java –tiedoston.
Tämän raportin pankkijärjestelmä esimerkissä käytetään JAAStutorialin tiedostoja.
Tutoriaali löytyy Sunyhtiön verkkosivulta [Sun05]. Tämän autentikointiesimerkin läh
dekoodi on raportin liitteessä 6.
5.3 Autentikointi AspectJratkaisuna
AspectJratkaisuna autentikointi voidaan toteuttaa uudelleenkäytettävällä abstraktilla
perusaspektilla, jota voi käyttää erilaisten järjestelmän autentikointiin. Perusaspektissa
määritellään ilmentymämuuttujaan autentikoitu subjekti kirjautumisvaiheessa. Autenti
koitu subjekti saa arvokseen LoginContextolion.
Perusaspektille kirjoitetaan aliaspekti, joka laajentaa sen järjestelmäkohtaiseksi. Alias
pektiin määritellään pointcutlohko, jossa siepataan autentikointia tarvitsevat järjestel
män operaatiot.
Pankkijärjestelmän autentikoinnin mahdollistavat AbstractAuthAspect aspekti
ja konkreettinen BankingAuthAspect aliaspekti. Näiden lähdekoodi on liitteessä 7.
51
AbstractAuthAspectaspektissa määritellään ilmentymämuuttujaan autentikoitu
subjekti kirjautumiskontekstin yhteydessä:
LoginContext lc =
new LoginContext("Sample",
new TextCallbackHandler());
lc.login();
_authenticatedSubject = lc.getSubject();
Kirjautuminen tapahtuu kerran ja on voimassa koko prosessin ajan kaikille operaatioille.
Autentikoitu subjekti –muuttuja voidaan siirtää myös käyttäväksi muualla, esimerkiksi
servletin istuntoobjektiin. Järjestelmästä uloskirjautuessa subjektimuuttujan arvoksi
pitää asettaa null.
AbstractAuthAspectaspektissa esitellään abstrakti authOperation() –
poincut, joka määritellään aliaspektissa sieppaamaan autentikointia tarvitsevat operaati
ot. Perusaspektin before advice varmistaa, että järjestelmä suorittaa autentikoinnin
kerran ohjelman ajon aikana. Jos _authenticatedSubject arvo on null, auten
tikointi suoritetaan, jos arvo on not null, autentikointia ei suoriteta. Näin järjestel
mässä toteutuu justintime autentikointi, eli se suoritetaan vain silloin, kun jotain järjes
telmän palvelua tarvitaan, eikä siihen ole vielä kirjauduttu.
Konkreettisessa aliaspektissa määritellään pointcutlohko, jossa siepataan autentikointia
tarvitsevat järjestelmän operaatiot. BankingAuthAspectaspektin execution –
advice voidaan määritellä sieppaamaan myös ohjelman käynnistävän main() –
metodin, jolloin käyttäjätunnukset kysytään ensimmäiseksi.
52
5.4 Auktorisointi
Auktorisointiprosesi päättelee onko käyttäjällä oikeudet käyttää hänen haluamiaan jär
jestelmän toimintoja. Auktorisoinnin esiehtona on että käyttäjä on autentikoitu.
5.4.1 JAASpohjainen auktorisointi luokkatoteutuksena
JAASpohjaisessa auktorisoinnissa järjestelmä saa autentikoinnin alijärjestelmältä tar
kistetun käyttäjän subjektin. Tämä Subject –luokan ilmentymä kapseloi käyttäjän tie
toja kuten tunnisteen ja valtuudet. Kaikkien auktorisointia tarvitsevien operaatioiden on
tarkistettava subjektin valtuudet.
Jokainen auktorisoinnin tarkistamista tarvitseva metodi on kapseloitava action
objektiin, joka toteuttaa PrivilegedAction tai PrivilegedExceptionAc
tion rajapinnan. Näillä molemmilla on ainoastaan run() –metodi, joka suorittaa ha
lutun operaation. Rajapintojen ainoa ero on poikkeuksen käsittely, PrivilegedAc
tion ei sisällä poikkeusten julistusta (declaration) lainkaan, mutta Privilege
dExceptionAction voi heittää Exceptiontyyppisen poikkeuksen.
Action –objekti suoritetaan autentikoidun subjektin puolesta käyttämällä Subject –
luokan staattista doAsPrivileged(Subject, PrivilegedAction, Ac
cessControlAccess) metodia. Sen keskimmäinen toimintoparametri voi olla
myös PrivilegedExceptionAction, jolloin run()metodin heittämä tarkistet
tu poikkeus kääritään ennen sen heittämistä.
Auktorisoitua toimintoa tarvitsevien metodien on tarkistettava subjektin valtuudet kut
sumalla AccessController.checkPermission() –metodia ja välitettävä
permissionobjekti, joka sisältää vaaditun valtuuden. Jos käyttäjällä ei ole valtuuk
sia, metodi heittää tarkastamattoman AccessControlExceptionpoikkeuksen.
Auktorisoitavalle järjestelmälle on määriteltävä käyttöoikeustiedosto (policy file), joka
asettaa eri subjekteille valtuudet tiettyihin operaatioihin. AccessControl
53
ler.checkPermission() –metodi käyttää tätä tiedostoa ja sallii operaatioiden käy
tön vain mikäli subjektilla niihin valtuudet.
Edellä esitellylle pankkijärjestelmälle on auktorisoinnin toteuttamiseksi kirjoitettu Ban
kingPermission –luokka, jonka konstruktorin String name –muuttuja välittää
käyttäjän valtuudet. Muuttujan avulla luetaan käyttöoikeustiedostosta käyttäjän valtuu
det. Toisen konstruktorin actionparametria käytetään instantioimaan valtuutuskohde
käyttöoikeustiedostosta. BankingPermission –luokan lähdekoodi on liitteessä 8.
Liitteessä 8 on myös esimerkki pankkijärjestelmän käyttöoikeudet määrittelevä secu
rity.policy tiedosto, jossa määritellään testUser käyttäjälle oikeudet käyttää
kaikkia tilinkäyttöön liittyviä operaatioita.
5.4.2 JAASpohjainen auktorisointi AspectJtoteutuksena
JAASpohjainen auktorisointi toteutetaan reitittämällä auktorisoidut kutsut sellaisen
luokan välityksellä joka implementoi joko PrivilegedExceptionAction tai
PrivilegedActionpoikkeuksen, riippuen siitä heittääkö operaatio tarkistetun
poikkeuksen. Workerobjektin luonti suunnittelumalli nopeuttaa tämän toteuttamista,
koska voidaan kirjoittaa aspekti, joka suorittaa operaatiot workerobjektin välityksellä
Subject.doAsPrivileged() –metodin kautta.
Auktorisointi voidaan modularisoida yhteen aspektiin kirjoittamalla uudelleen käytettä
vä abstrakti aspekti, joka lisää auktorisoinnin järjestelmään. Tälle perusaspektille kirjoi
tetaan järjestelmäkohtainen konkreettinen aliaspekti. Näin saadaan järjestelmä auktori
soitua vähäisellä koodimäärällä. Abstrakti perusaspekti toteuttaa auktorisoinnin ohella
myös autentikoinnin. Esimerkkinä tällaisesta perusaspektista on AbstractAuthAs
pectaspekti, jonka lähdekoodi on liitteessä 8.
AbstractAuthAspectaspekti ohjaa kaikki auktorisointia tarvitsevien operaatioi
den kutsut PrivilegedExceptionActionrajapinnan toteuttavan anonyymin luo
54
kan kautta. Lisäämällä proceed()metodi workerobjektin suorittavaan run()
metodiin, kääritään operaation argumentit, joten ne voivat olla mitä tahansa tyyppiltään
ja lukumäärältään.
AbstractAuthAspect –aspektissa esitellään abstrakti pointcut authOpera
tions(), konkreettisessa aliaspektissa tämä pointcut määrittelee auktorisointia tarvit
sevat operaatiot. Nämä operaatiot ovat samoja, jotka tarvitsevat autentikointia. Kun
pelkkä sisäänkirjautuminen ohjelmistoon ei riitä resurssien käytön valtuudeksi, operaa
tiot voidaan eritellä esimerkiksi seuraavasti:
pointcut authenticatedOperations()
:primaryAuthenticatedOperations()
|| authorizedOperations();
Abstraktissa perusaspektissa esitellään abstrakti getPermission() –metodi, joka ha
kee tarvittavat valtuudet. Myös tämä metodi määritellään konkreettisessa aliaspektissa.
Metodi välittää valtuudet operaatioiden suorittamiseen.
Abstraktin perusaspektin around advice suorittaa siepatut operaatiot autentikoidun
subjektin puolesta. Se luo auktorisointia tarvitseville operaatioille workerobjektin ja
suorittaa ne run()metodissaan. Perusaspektin RuntimeException –poikkeukset
kääritään joko AuthorizationException tai AuthenticationException
tyyppisiksi poikkeuksiksi.
Konkreettisessa aliaspektissa määritellään järjestelmäkohtainen auktorisointi. Aliaspek
tiin määritellään vain edellä kerrotulla tavalla authOperations()pointcut ja get
Permission() –metodi. Metodi välittää valtuudet operaatioon joinpointkohdan
sieppaaman operaation nimen perusteella. Pankkijärjestelmän esimerkin konkreettin
BankingAuthAspect –aliaspektin lähdekoodi on liitteessä 8.
55
6 TRANSAKTIO
Tässä luvussa havainnollistetaan transaktion hallinnan ongelmia. Transaktio koostuu
atomisista toimenpiteistä, jotka takaavat että järjestelmä säilyttää ristiriidattoman tilansa
ennen toimenpidettä ja sen jälkeen. Atomisuus, eheys, erillisyys ja kestävyys ovat trans
aktiolta vaadittuja ominaisuuksia.
Esimerkkinä toteutetaan edellä kerrotun osittaisen pankkijärjestelmän transaktion hallin
ta. Ensin kerrotaan peruslähtökohdat ja esitellään perinteinen ratkaisu sekä AspectJ
ratkaisu. Transaktion hallinta on toteutukseltaan järjestelmän poikkileikkaava tehtävä,
joten ratkaisun modularisointi selkeyttää sen toteutusta ja ylläpitoa.
6.1 Ydin tehtävien toteutus
Pankkijärjestelmän vaatimuksena on pysyvyys ja tietokannan eheys. Yhteys tietokan
taan pitää olla taloudellinen. Käyttäjän pitää pystyä suorittamaan haluamansa operaatio
loppuun. Mikäli se ei onnistu, jo tehdyt toimenpiteet perutaan.
Tässä esimerkissä käytetään JDBC yhteyttä MySQLtietokantaan. Tietokannan taulu
jen luominen ja konfigurointi on esitetty liitteessä 1.
Kuva 7 esittää UMLnotaatiolla järjestelmän peruslähtökohdat. Kappaleessa 6.2 kerro
taan transaktion perinteisestä ratkaisusta ja kappaleessa 6.3 AspectJratkaisusta.
56
Kuva 7. Pankkijärjestelmän perusluokat
6.2 Transaktion perinteinen ratkaisu
Tämän pankkijärjestelmän toiminnallinen ydin on toteutettu perinteisillä javaluokilla.
Järjestelmässä on tilin ja tilinsiirron toteuttavat luokat, sekä tietokantayhteyden ja trans
aktion päivityksen hyväksyvä luokka. Esimerkin lähdekoodi on liitteessä 9.
AccountJDBCImplluokka on Accountrajapinnan konkreettinen toteutus. Luokka
sisältää rajapinnan metodien toteutuksen lisäksi privatemääreisen metodin, joka
asettaa tilin saldon. Luokan metodit sisältävät transaktioita. Tämän luokan sisällä meto
dien suorituksessa ei tarvitse huomioida muiden metodien tekemiä päivityksiä tietokan
taan.
InterAccountTransferSystemluokan ainoa metodi siirtää rahamäärän tililtä
toiselle. Tällöin ohjelman suorituksessa tapahtuu sisäkkäisiä transaktioita (nested
transactions), mikä vaatii metodien suoritusten koordinointia.
Transaktion eheyden vaatimuksen täyttymiseksi sekä credit() että debit()
metodien suorituksen täytyy onnistua tai päivitykset tietokantaan pitää perua.
57
DatabaseHelperluokassa Connectionluokan getConnection() –metodi
palauttaa tietokantayhteyden objektina. Metodissa voidaan määritellä päivitystapa tieto
kantaan. Jos sen arvoksi on asetettu connection.setAutoCommit(true), tieto
kannan päivityksiä ei tarvitse hyväksyä ohjelmallisesti, vaan se tapahtuu välittömästi
ohjelman suorituksessa. DatabaseHelper.getConnection()metodi luo aina
uuden yhteyden jokaiselle tietokantaoperaatiolle. Siksi ei voida tehdä useita päivityksiä
yhden transaktion sisällä, koska jo tapahtuneita päivytyksiä ei peruta vaikka operaatios
sa yksi päivitys epäonnistuu. Näin ollen järjestelmä voi jäädä virheelliseen tilaan ope
raation epäonnistuessa. Tämän estämiseksi pitää autocommit arvoksi asettaa false.
Lisäksi samaa yhteysobjektia pitää käyttää kaikille saman operaation tietokannan päivi
tyksille ja hyväksyä ne kerralla, jos kaikki ovat onnistuneet tai perua, jos jokin päivitys
epäonnisui.
Jos samaa DatabaseHelperluokan luomaa yhteysobjektia käytetään koko operaati
on ajan. Tämän toteuttamiseksi on kaksi yleistä tapaa; välitetään yhteysobjekti operaati
oon osallistuville metodeille argumenttina tai säiekohtaisen varaston käyttäminen.
Yhteysobjektin välittäminen metodeille vaatii melkoisia muutoksia luokkien toteutuk
seen. Tässä esimerkissä yhteys voidaan luoda InterAccountTransferSystem
luokan transfer() –metodissa, joka kutsuu debit() ja credit()metodia, ja
antaa niille lisäargumentiksi yhteysobjekti. Metodien suoritusta ei hyväksytä erikseen,
vasta kun molemmat metodit on suoritettu onnistuneesti, yhteysobjekti kuitataan Con
nection –luokan commit()metodilla ja yhteys suljetaan. Seuraavassa nämä meto
din koodiin tehdyt muutokset:
public static void transfer(Account from, Account to,
float amount)
throws InsufficientBalanceException {
Connection conn = DatabaseHelper.getConnection();
conn.setAutoCommit(false);
try {
58
to.credit(amount, conn);
from.debit(amount, conn);
conn.commit();
}
catch (Exception ex) {
conn.rollback();
// log exception jne
}
finally {
conn.close();
}
}
Näistä muutoksista aiheutuu edelleen muutoksia credit() ja debit()metodeihin.
Laajemmassa järjestelmässä tällaisten muutosten tekeminen voi olla hyvin työläs.
Toinen edellistä modularisoidumpi tapa välittää yhteysobjekti lisäargumenttina on
käyttää ThreadLocalluokan tarjoamaa säikeiden varastointia. Tällöin on Data
baseHelperluokaan lisättävä yhteysobjektin käärivä static final Thread
Local jäsen, jolta DatabaseHelper.getConnection()metodi määritellään
pyytämään yhteys. Metodi palauttaa varastosta säiekohtaisen yhteyden, jos sellainen on
saatavilla. Jos ei ole, metodi luo uuden yhteyden ja asettaa sen varastoon. Tämä tapa
modularisoi yhteyden käyttämisen, mutta ei transaktiota tehtäväkokonaisuutena.
Jos vaaditaan, että sisäkkäisessä transaktiossa vain ylätason operaatio voi suorittaa kuit
tauksen, on tarpeen määritellä ohjelmallisesti kuittauksen suorittaja. Yksittäistä metodia
ei voi määritellä ylätason operaatioksi, koska jokaiseen transaktioon pitäisi määritellä
metodien taso erikseen. Eräs ratkaisu on käyttää säikohtaista muuttujaa tai lisäargu
menttia, joka ilmaisee kutsun tason tai syvyyden. Kutsun tason mittaria kasvatetaan
59
metodin suoritukseen mentäessä ja vähennetään suorituksen päättyessä, jolloin nollata
solle palattaessa voidaan suorittaa kuittaus.
Yhteenvetona tästä pankkijärjestelmän osan perinteisestä ratkaisusta voidaan huomata,
että sen transaktion hallinnan koodi on hajanaisesti useissa metodeissa ja moduleissa, ja
sen muunneltavuus on sidottu liiketoimintalogiikkaan.
6.3 AspectJkielinen transaktion ratkaisu
Tässä luvussa muodostetaan edellä esitetystä pankkijärjestelmän osasta AspectJ
kielinen ratkaisu. Järjestelmän perusluokat ovat samat kuin perinteisen järjestelmän rat
kaisua esittävässä kuvassa 7, joka esitettiin luvussa 6.1.
Järjestelmän korkean tason vaatimuksena on:
Ratkaisun on oltava uudelleen käytettävä. Tätä ratkaisua käytetään muissakin
JDBCpohjaisten järjestelmien kehitysprojekteissa.
Transaktion päivitysoperaatioiden on käytettävä yhtä yhteysobjektia, joka sisäl
tää transaktion tilan. Kun metodin kontrollivirrassa tarvitaan transaktion hallin
taa, yhteyden luonnin jälkeen kaikki tämän kontrollivirran päivitykset käyttävät
samaa yhteysobjektia. Näin tämä yhteysobjekti muodostaa operaatioiden kon
tekstin.
Kaikki transaktion päivitykset hyväksytään, kun ylätason operaatio päättyy on
nistuneesti. Jos jokin päivitys epäonnistuu tai jokin liiketoimintametodi heittää
poikkeuksen, yhteysobjektin kaikki päivitykset on peruttava (roll back).
Ratkaisun on uudelleen käytettävyys toteutuu luomalla abstrakti aspekti, joka toteuttaa
pääpiirteet transaktion logiikasta. Logiikan lisäksi tämä aspekti sisältää abstraktit point
cut –esittelyt transaktion operaatioille ja yhteyden saamiseksi.
Ylätason operaatioiden sieppaamiseksi määritellään pointcut, joka sieppaa ne ajon ope
raatiot jotka eivät jo ole toisen transaktion operaation kontrollivirrassa. Ylätason operaa
60
tioiden onnistumista valvotaan poikkeuksien hallinnalla. Transaktion operaatioiden suo
ritus etenee around advice –määrittelyn try/catch –lohkossa. Operaatio on onnistunut
mikäli se ei heitä yhtään poikkeusta.
Yhden yhteysobjektin käytön varmistamiseksi transaktion sisällä määritellään advice
ohje transaktion kontrollivirrassa yhteyden luovalle joint point –kohdalle. Kun yhteyttä
tarvitaan ensimmäisen kerran siepatussa operaatiossa, yhteys hankitaan ja se varastoi
daan aspektin sisällä ilmentymä muuttujaan (instance variable). Kun yhteyttä tarvitaan
seuraavan kerran, advice palauttaa varastoidun yhteyden ja välittää siepatun operaation.
Aspekti assosioidaan myös ylätason operaatioiden kontrollivirtaan. Koska aspekti varas
toi yhteysobjektin jota käytetään kaikkiin päivityksiin, aspektin ilmentymä sisältää
transaktion kontekstin niin kauan kuin transaktio kestää.
Transktiota tarvitsevan järjestelmän osan toteutukseen määritellään konkreettinen ali
aspekti. Aliaspektissa määritellään konkreettinen pointcut, joka sieppaa perustoteutuk
sesta transaktion join point –kohdat ja konkreettinen pointcut, joka sieppaa yhteyden
muodostamisen join point –kohdat.
Yhteyden luomiseen voidaan käyttää erilaisia tapoja, kuten apuluokkia tai resurssien
säiliöitä (resource pooling). Tämän vuoksi perusaspektissa esitellään yhteyksiä varten
abstrakti pointcut, joka määritellään konkreettisessa aliaspektissa. Kuva 8 esittää UML
notaationa pankkijärjestelmän transaktion hallinnan aspekteja.
61
Kuva 8. Pankkijärjestelmän transaktion hallinnan aspektit.
6.3.1 JDBCTransactionAspectaspektin koodin selitys
Tämän AspectJkielisen ratkaisun lähdekoodi on liitteessä 10. JDBCTransac
tionAspect –aspektin selitys kohdittain:
percflow(topLevelTransactedOperation()) assosioi aspektin ilmenty
män transaktion ylätason operaation kontrollivirtaan. Aliaspektin transactedOpe
ration()–pointcut määrittelee transaktion operaatiot, joita kutsuttaessa ilmentymä
luodaan jos se ei kuulu jo meneillään olevaan transaktioon. Ilmentymä säilyttää transak
tion tilan. Tätä ratkaisua voidaan käyttää, jos transaktioon osallistuu vain yksi järjestel
mä.
Abstraktit obtainConnectionpointcut ja transactedOperation()–pointcut
määritellään konkreettisessa aliaspektissa. Nämä käsittelevät yhteyden luomisen ja ope
raatioiden suorituksen.
topLevelTransactedOperation()pointcut määritellään ylätason operaatioksi.
Transaktion hallinnan suorittamiseksi sen tukena on abstrakti transactedOpera
tion()pointcut, sekä Object around advice. Aspektin uusi instanssi assosioi
62
daan ylätason operaatioon. Kun sellaisen join point –kohdan suoritus alkaa, luodaan
konkreettisen aliaspektin uusi instanssi.
Object around –advice, joka ohjeistaa topLevelTransactedOperation(),
vie siepatun operation try/catch lohkoon. Trylohkossa kutsutaan proceed() –
metodia operaation suorituksen jatkamiseksi. Jos operaatio heittää suorituksen aikana
poikkeuksen, suoritus siirtyy catchlohkoon joka kutsuu yhteysobjektin roll
back()metodia, jolloin päivitykset perutaan. Finallylohko sulkee yhteyden.
if(_connection != null) käsittelee tapaukset joissa yhteyttä tietokantaan ei
luotu, koska liiketoimintalogiikka ei tarvinnut päivityksiä tai kyselyjä tietokannasta.
(Ylätason operaatio voi sisältää tällaisia alioperaatioita suorituksensa loppuvaiheessa.)
Tässä transaktion hallinnan ratkaisussa samaa yhteyttä käytetään operaation kaikkiin
päivityksiin, jolloin ne voidaan hyväksyä kerralla kutsumalla yhteysobjektin com
mit()metodia. Connection around() –advice tarkistaa, onko connection –
instanssin arvo null. Mikäli se on null, ylätason operaation suoritus tarvitsee en
simmäisen kerran yhteyttä. Seuraavaksi advicelohko hankkii yhteyden ja kytkee sen
automaattisen hyväksynnän pois päältä metodissa setAutoCommit(false). Jos
operaatio on onnistunut yhteysobjekti kuitataan commit()metodilla. Perättäisille
yhteysobjektin kutsuille advicelohko palauttaa varastoidun yhteysobjektin sen sijaan,
että loisi uuden.
TransactionException määritellään ajonaikaisena poikkeuksena. Tämän poikke
uksen heittäminen ilmaisee kutsujalle, että transaktio on epäonnistunut ja kääritty poik
keus ilmaisee epäonnistumisen syyn.
Poikkeusten pehmentämisellä vältytään try/catchlohkojen määrittelyltä rollback()
ja close()metodien kutsuissa.
Koska tässä ratkaisussa vain JDBCTransactionAspect aspekti suorittaa kutsut
yhteysobjekteille, määritellään koodausta ohjaava illegalConnectionManage
ment()poincut ja around() advicelohko, jotka käsittelevät aspektin ulkopuolelta
63
määritellyt virheelliset kutsut. Tällaisia kutsuja voisi tehdä esimerkiksi pankkijärjestel
män määrittelyssä kirjoittamalla uusia transaktioon liittyviä hyväksymiskäytäntöjä kut
suen Connection.setAutoCommit()metodia.
illegalConnectionManagement()poincut sieppaa kaikki yhteyden hallinnan
join point –kohdat ja around() advicelohko estää ohjelmoijaa käyttämästä niitä. Ad
vicelohkon sijaan tai sen ohella voidaan määritellä myös käännöksen aikaisen virheen
tulostus declare error –lauseella seuraavasti:
declare error : illegalConnectionManagement()
: “Do not call close(), commit(), rollback(),or setAutoCommit() on Connectin objects fromhere”;
6.3.2 BankingTransactionAspectaspektin koodin selitys
Suurin osa transaktion toiminnallisuudesta on määritelty abstraktissa JDBCTransac
tionAspectaspektissa. Sitä laajentavaan konkreettiseen BankingTransactionAs
pectaspektiin tarvitaan vain kaksi pointcutmäärittelyä pankkijärjestelmän transaktion
hallinnan toteuttamiseksi.
transactedOperation()pointcut sieppaa transaktion tukea tarvitsevien metodien
suorituksen ja optainConnection()pointcut määrittelee kaikki metodien kutsut
joita käytetään tietokanta yhteyksien saamiseksi.
6.4 Yksi transaktio – useita alijärjestelmiä
Yhdessä transaktiossa joudutaan usein suorittamaan operaatioita, joihin osallistuu useita
alijärjestelmiä. Lisäksi voi olla vaatimuksena että jokaisella alijärjestelmällä on oma
aliaspekti, jonka poincutlohkot määrittelevät transaktion tukea tarvitsevat operaatiot.
64
Näin alijärjestelmiin tehtävät muutokset aiheuttavat transaktion osalta muutoksia vain
aliaspektissa. Tällöin on varmistettava etteivät muutokset aiheuta ristiriitoja transaktion
hallinnassa.
Näiden vaatimusten ratkaisemiseksi voidaan käyttää Osallistujasuunnittelumallia, jon
ka mukaisesti tehdään jokaiselle transaktion osapuolena olevalle alijärjestelmälle trans
aktiota tukeva aliaspekti. Nämä sisäaspektit laajentavat abstraktin JDBCTransac
tionAspect perusaspektin. Ne voidaan toteuttaa itsenäisinä aliaspekteina, tai luokki
en sisäaspekteina.
Pankkijärjestelmä esimerkkissä AccountJDBCImpl ja InterAccountTransfer
System luokat ovat toiminnallisesti erillisiä alijärjestelmiä, joten niihin voidaan sovel
taa edellä kerrottua Osallistujasuunnittelumallia. Tässä esimerkissä aliaspektit lisätään
alijärjestelmien luokkiin sisäaspekteina. Järjestelmän rakennetta havainnollistaa kuva
9.
6.4.1 TransactionParticipantAspectaspektin selitys
Liitteessä 11 on AccountJDBCImpl ja InterAccountTransferSystem
luokkien tämän ratkaisutavan lähdekoodi.
Näiden luokkien TransactionParticipantAspectsisäaspektit laajentavat
JDBCTransactionAspectaspektin. Tämän laajennusominaisuuden vuoksi In
terAccountTransferSystemluokan sisäaspektissa joudutaan määrittelemään
yhteyden hakeva obtainConnection()pointcut, joka ei sisällä yhtään join point
kohtaa, koska tämä luokka ei saa hakea koskaan itse yhteyttä. Luokan sisäaspektien
transactedOperation()poincut sieppaa luokansa transaktiota tarvitsevien ope
raatioiden suoritukset.
Vaikka tämä ratkaisutapa mahdollistaa transaktion hallinnan kapseloinnin JDBCTran
sactionAspect perusaspektiin, voi syntyä ajonaikaisia ongelmia jos eri osapuolten
aliaspektien instanssit eivät käytä samaa yhteysobjektia. Näin ollen vain osa operaatios
65
ta saattaa onnistua, esimerkiksi tilinsiirrossa vastaanottajan tilia voidaan hyvittää vaikka
maksavan tilin veloitusoperaatio on epäonnistunut.
Jotta transaktion hallinnassa voidaan käyttää useita alijärjestelmien aspekteja, on var
mistuttava että transaktion päivityksiin käytetään samaa yhteysobjektia silloinkin, kun
eri aspektit sieppaavat transaktion aikana erillisiä operaatioita. Päivityksestä ja huoleh
ditaan transaktion toteuttavassa perusaspektissa, johon tehtävistä muutoksista kerrotaan
tarkemmin seuraavassa luvussa.
66
Accountluokka
+credit( amount:float)+debit( amount:float)+getAccountNumber()+getBalance()
AccountJDBCImplluokka
+credit( amount:float )+debit( amount:float )+getAccountNumber()+getBalance()setBalance()+<<aspect>>TransactionParticipantAspect( )
DatabaseHelperluokka
getConnection( )
TransactionParticipantAspect
#<<pointcut>>transactedOperation()#<<pointcut>>optainConnection()
InterAccountTransferSystemluokka
+transfer( account1:Account, account2:Account : Account, amount:float)+<<aspect>>TransactionParticipantAspect ( )
impelments
sisäaspektisisäaspekti
JDBCTransactionAspect_connection : Connection
#<<pointcut>>transactedOperation()#<<pointcut>>obtainConnection()#<<pointcut>>inTransactedOperation(TransactionContext)<<advice>>around():transactedOperation() && !inTransactedOperation(TransactionContext)<<abstract class>>TransactionContext extends RunnableWithReturn() : Connection
extends
RunnableWithReturn implements Runnable
getReturnValue: Object( )
Kuva 9. Pankkijärjestelmä sisältää kaksi transaktioon osallistuvaa alijärjestel
mää, joiden transaktion hallintaa tuetaan sisäaspekteilla.
67
6.4.2 Usean alijärjestelmän transaktion toteuttava perusaspekti
Edellä luvussa 6.3 esitetyssä versiossa abstrakti JDBCTransactionAspect
perusaspekti huolehti kahdesta roolista. Se varastoi yhteysobjektin, johon kaikki trans
aktion operaatiot pystyvät viittaamaan sekä huolehti transaktion hyväksymisestä tai pe
rumisesta. Tässä luvussa esitellään perusaspekti, jossa nämä roolit on eroteltu. Aspekti
määritellään vastuulliseksi pelkästään transaktion hyväksymisestä ja perumisesta, ja
käytetään hyväksi erillistä transaktion kontekstin objektia, joka varastoi yhteyden.
Tämän erillisyyden toteuttamiseksi perusaspektissa käytetään oletusarvoista assosiointia
percflowassosiaation sijaan, sekä poistetaan _connection muuttuja aspektista.
Oletusarvoinen (default) assosiaatio johtaa siihen, että jokaisesta aliaspektista luodaan
korkeintaan yksi instanssi ohjelman suorituksen aikana. Koska _connection
instanssi poistetaan aspektista se on tilaton aspekti, eivätkä aspektin instanssit tässä
ratkaisussa ilmaise transaktion kontekstia. Workerobjektin luonti suunnittelumallia
käytetään luomaan automaattisesti Workerobjekti jokaiselle uudelle transaktiolle. Wor
kerobjektin tehtävänä on varastoida yhteysobjekti, näin Workerobjekti palvelee trans
aktion kontekstina.
Workerobjekti luodaan transaktion hallintaa tarvitseville ylätason operaatioille ja siten
käsitellään tätä operaatiota workermetodina. Workerobjektin run()metodi sisältää
workermetodin joka sisältää transaktion hallinnan logiikan, eli operaation hyväksymi
sen tai perumisen. Kun suoritetaan ensimmäinen transaktion hallintaa tarvitseva metodi,
luodaan yhteysobjekti joka varastoidaan Workerobjektiin. Toteutuksessa käytetään
contextobjektia varmistamaan, ettei luoda ylimääräistä yhteysobjektia kun Worker
objektin run()metodin kontrollivirrassa saadaan käsiteltäväksi jokin uusi join point –
kohta.
Aspektiin implementoidaan TransactionContextluokka, joka laajentaa Runna
bleWithReturnluokan. Jokaisen konkreettisen aliluokan pitää implementoida
run()metodi, joka suorittaa workermetodin ja asettaa _returnValuejäsenen ope
raation tulokseen. Uusi contextobjekti luodaan kaikille transaktion aloittaville ope
68
raatioille elleivät ne jo ole transaktion kontekstin run()metodin suorituksen kontrolli
virrassa. Kukin transaktioon osallistuva aspekti voi luoda transaktion kontekstin, uutta
kontekstia ei luoda operaatiolle, joka on jo osa transaktion kontekstia. Koska transaktion
hallinta suoritetaan vain contextobjektin kautta, uutta kontekstia ei luoda sisäkkäisil
le operaatioille. Liitteessä 11 on tämän usean alijärjestelmän transaktion esimerkin läh
dekoodi.
6.4.3 Muutetun JDBCTransactionAspectaspektin koodin selitys
Aspektin percflowassosiaatio on muutettu oletusarvoiseksi assosiaatioksi. Aspekti
sisältää abstraktit transactedOperation() ja obtainConnection()
pointcut esittelyt, jotka määritellään konkreettisissa aspekteissa.
inTransacteOperations()pointcut sieppaa TransactionContext.run()
metodin kontrollivirran ja taltioi metodiin assosioidun objektin, joka välitetään kutsu
pinoon. Tämä pointcut tutkii kuuluuko operaatio jo meneillään olevaan transaktioon.
Object around() advice määrittelee transactedOperations()ja inTran
sacteOperations()pointcut määrittelyjen perusteella ne join point –kohdat, jotka
eivät vielä ole transaktion kontekstin suorituksessa ja luo tarvittaessa uuden transaktion
kontekstin. Lohkon run()metodin määrittely on samanlainen kuin ensimmäisessä
versiossa. Tässä toteutuu workerobjektin luonti.
Connection around()advice ohjeistaa yhteyden hankkimista ja tarvitsee suori
tuksessa olevan transaktion kontekstin objektia, jotta se voi käyttää (access) sen
_connectionobjektia. inTransacteOperations()pointcut sieppaa tämän
kontekstin. Tässä on sovellettu madonreikäsuunnittelumallia luomaan suora yhteys
transaktion kontekstin objektin run()metodin ja yhteyttä tarvitsevan metodin välille.
Abstrakti TransactionContextluokka laajentaa RunnableWithReturn
luokan. Luokan _connection muuttuja säilyttää transaktion yhteysobjektin.
69
6.5 JTApohjainen transaktion hallinta
Tässä raportissa edellä esitellyt järjestelmät perustuivat JDBCpohjaiseen transaktion
tukeen. Nykyaikaisissa tietojärjestelmissä vaatmukset ovat kuitenkin laajempia. Usein
tarvitaan hajautettua transaktion hallintaa ja mukana on useita resursseja sekä tietokan
toja. Operaatioissa joudutaan päivittämään myös muiden järjestelmänosien tai ERP (En
terprise Recourse Planning) –järjestelmien tietokantoja. Näissä tapauksissa transaktio
käsittelee useita yhteysobjekteja.
Transaktio voi ulottua myös monenlaisiin resursseihin. Esimerkiksi samassa transakti
ossa voidaan päivittää tietokantaa ja lähettää viestejä jonoon käyttäen JMS (Java Mes
sage Service) –palvelua. Tällöin sekä tietokannan päivityksen että viestijonon lähetyk
sen pitää onnistua, jotta järjestelmä säilyttää ehyen (consistent) tilansa. Nämä tapaukset
vaativat (Transaction Processing) TPmonitorin käyttöä, jolla koordinoidaan resurssien
transaktion onnistumista. Tällöin resurssien (kuten tietokantojen ja JMS viestityksen
väliohjelmistojen) pitää pystyä yhteistyöhön TPmonitorin kanssa. Monitoroinnissa
ohjelmallisten rajapintojen käyttö, kuten JTA (Java Transction API), mahdollistavat TP
monitorin soveltavan käytön erilaisissa ympäristöissä.
Käytettäessä puhtaasti oliosuuntaunutta ohjelmointitekniikkaa, JTA ei mahdollista
transaktion tehtävien eriyttämistä ydintehtävistä. Tämä johtuu siitä, että toteutuksessa
transaktion luonti, hyväksyminen ja peruminen leviävät kaikkiin moduleihin, joissa on
transaktion hallintaa vaativia operaatioita. JTA tarjoaa läpinäkyvyyttä käytettävään TP
monitorointiin, mutta vaatii sen API:n kutsumista useista ydinmoduuleista.
AspectJkielen käyttö yhdessä JTA:n kanssa mahdollistaa transaktion hallinnan modula
risoinnin poikkileikkaukselliseksi tehtäväksi. Liitteessä 12 on abstrakti aspekti JTA
TransactionAspect, joka kapseloi kaikki JTApohjaiset transaktiot. Tuekseen se
tarvitsee yhden tai useampia konkreettisia aliaspekteja, joissa määritellään transac
tedOperation()pointcut sieppaamaan transaktion tukea tarvitsevat operaatiot.
70
Tämä perusaspekti poikkeaa JDBCTransactionAspectaspektista vain seuraavilta
osin:
JTATransactionAspectaspektissa käytetään transaktion hallintaan UserTran
saction –objektia, joka tarjoaa API:n transaktion hyväksymiseen ja perumiseen.
Transaktio alussa kutsutaan UserTransaction.begin(), hyväksyttäessä User
Transaction.commit(), ja peruttaessa UserTransaction.rollback().
Tässä aspektissa ei tarvita pointcut ja advicelohkoa takaamaan saman yhteysobjektin
käyttöä. JTA:ta käytettäessä ei yksittäiseen päivitykseen tarvitse käyttää vain yhtä re
surssia, kuten tietokantayhteyttä.
Tätä aspektia käytettäessä on käännösympäristössä hyvä käyttää valvovia menettelyta
pojen sääntöjä takaamaan, ettei aspektia käytetä muille kuin JTA:han sopiville resurs
seille.
71
7 YHTEENVETO
Aspektisuuntautunut ohjelmointitekniikka mahdollistaa ohjelmiston toiminnallisuuden
modularisoinnin aspektien avulla. Aspekti laajentaa oliosuuntautuneen ohjelmoinnin
luokkien käyttötapaa toteutuksessa. Toteutusta voidaan tiivistää muutamaan aspektiin
sen sijaan, että koodaus leviäisi laajalle ohjelmiston rakenteeseen. Modularisointi sel
keyttää ohjelmiston rakennetta ja parantaa sen uudelleen käytettävyyttä. Perinteiset
suunnittelumallit ovat silti edelleen käytettävissä.
Tämän raportin esimerkeissä esitetyt aspektit ovat rakenteeltaan uudelleen käytettäviä
ratkaisuja. Niissä toteutetaan käyttäjän kirjautuminen järjestelmään ja auktorisointi, jos
sa käyttäjälle annetaan valtuuksia käyttää tiettyjä resursseja ja järjestelmän operaatioita,
joihin tarvitaan transaktion tukea. Esimerkkien ratkaisut täyttävät modulaarisuuden vaa
timuksen, koska kukin ohjelmiston poikkileikkauksellinen tehtävä tai huolehdittava asia
on toteutettu selkeästi erillisessä ohjelman osassa, joka koostuu muutamasta aspektista.
Ratkaisut eivät vaadi suuria muutoksia sovellettaessa niitä samankaltaisiin järjestelmiin.
72
LÄHTEET
[ElF01] Elrad T., Filman r., Bader A., The Aspect Oriented Programming, Chi
cago,2001,(ACM 2005)
[HiH04] Hilsdale E., Hugin J., Advice Weaving in AspectJ, Lancaster UK, 2004
(ACM, 13.1.2005)
[Ken99] Kendall E. A., Role model designs and implementations with aspect
oriented programming, Denver US, October 1999, ACM 17.1.2005
[LaC03] Lafferty D., Cahill V., Languageindependent aspectoriented program
ming, Trinity College Dublin, October 2003 (ACM, 13.1.2005)
[Lad03] Laddad R., AspectJ in Action Practical AspectOriented Programming,
Manning Publications Co, USA, 2003.
[Sun05] JAAS Authentication Tutorial, viitattu 12.2.2005 , Saatavilla
http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/tutorials/GeneralAc
nOnly.html
[Xer03] Xerox Corporation, Palo Alto Research Center, The AspectJ Programming
Guide, viitattu 20.2.2005, saatavilla: http://dev.eclipse.org/viewcvs/ in
dextech.cgi/~checkout~/aspectjhome/doc/progguide/index.html.
OHJELMISTOT JA LÄHDEKOODIT:
[ECL05] AspectJ Compiler (aspectj1.2.1.jar) ohjelmisto, viitattu 15.1.2005 saata
villa: http://eclipse.org/aspectj/,
[ECL05a] Eclipse Platform SDK 3.0.1 ja Eclipse AspectJ Development Tools, viitattu
18.1.2005, saatavilla: http://eclipse.org/downloads/index.php
[MYS05] MySQL 4.1ohjelma, viitattu 28.2.2005 Saatavilla:
http://dev.mysql.com/downloads/
73
[Lea05] Lea Doug, JSR166luokkakirjasto, viitattu 10.3.2005 saatavilla:
http://gee.cs.oswego.edu/dl/concurrencyinterest/index.html.
[Lea05a] Lea Doug, Concurrentluokkakirjaston kotisivu, viitattu 10.3.2005, saata
villa:
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/ in
tro.html
[Lea03b] Lea Doug, AspectJ in Action kirjan esimerkkien lähdekoodi, saatavilla:
http://www.manning.com/catalog/view.php?book=laddad&item=source
[Lea03c] Lea Doug, JSR 166 –ohjelmisto ja dokumentaatio, viitattu 10.3.2005, saa
tavilla: http://gee.cs.oswego.edu/dl/concurrencyinterest/index.html
[Lea03d] Lea Doug, backportutilconcurrent luokkakirjasto, viitattu 10.3.2005,
saatavilla: http://www.mathcs.emory.edu/dcl/util/backportutilconcurrent/
Muita resursseja:
Suunnittelumallien toteutuksia:
Implementations of GoF Design Patterns in Java and AspectJ, saatavilla:
http://www.cs.ubc.ca/~jan/AODPs/