Academiejaar 2006 - 2007
Departement Toegepaste Ingenieurswetenschappen Schoonmeersstraat 52 - 9000 Gent
Databank criminaliteit in de middeleeuwen: Normalisatie en Ontwikkeling bijbehorende hulpapplicaties Publicatie via internet
Eindwerk voorgedragen tot het behalen van het diploma van INDUSTRIEEL INGENIEUR INFORMATICA Wim Decoodt Promotoren:
Wijnand Schepens Guy Dupont
Woord vooraf
Bij de realisatie van deze thesis heb ik de hulp mogen ervaren van heel wat mensen en graag zou ik van dit voorwoord gebruik willen maken om deze te bedanken. Eerst en vooral wil ik de heer Dupont bedanken, hij is diegene die deze thesis mogelijk heeft gemaakt. Hij heeft heel wat tijd voor me vrijgemaakt, zodat we samen de huidige situatie en mogelijkheden naar de toekomst toe konden bespreken. Ook het logboek dat hij heeft opgesteld over de originele databank is in deze thesis erg nuttig gebleken. Graag zou ik ook de heer Schepens, mijn interne promotor, bedanken die me tijdens het verloop van deze thesis altijd met raad en daad heeft bijgestaan. Verder wil ik nog Leen en Jurgen bedanken, die veel tijd hebben gestoken in het herlezen van deze scriptie en Frederik voor het beschikbaar stellen van de LaTEX-templates.
Wim Decoodt Gent, 20 augustus 2007
i
Inhoudsopgave
Woord vooraf
i
Inleiding
v
I
Situatieschets
1
1 Probleemsituering
2
2 Doelstellingen 2.1 Specifieke eisen . . . . 2.2 De algemene structuur 2.3 Implementatietaal . . 2.4 Hosting webserver . .
4 4 5 7 7
II
. . . . van de . . . . . . . .
. . . . . . . applicaties . . . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
Conversie databank en ontwikkeling bijbehorende hulpapplicaties
3 Conversie databank 3.1 DBMS-keuze . . . . . . . . . . . . . . . 3.2 Implementatietaal . . . . . . . . . . . . 3.3 De structuur van de originele databank 3.4 De nieuwe databankstructuur . . . . . . 3.5 Beschrijving applicatie . . . . . . . . . . 3.6 Het benaderen van de databank . . . . . 3.7 De functionaliteit van de applicatie . . .
ii
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
8
. . . . . . .
9 9 9 10 14 19 20 21
iii 4 De 4.1 4.2 4.3
administratieapplicatie De huidige situatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De applicatie in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De applicatie via formulierweergave . . . . . . . . . . . . . . . . . . . . . . . .
22 22 22 23
5 De 5.1 5.2 5.3 5.4 5.5
kaartenapplicatie Inleiding . . . . . . . . . . . . . . Gebruikte technologie . . . . . . Het aanvullende databankschema De kaartenapplicatie versie 1 . . De kaartenapplicatie versie 2 . .
25 25 26 26 34 35
III
. . . . . . . . . . . . . . . . voor kaarten . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
De Webapplicatie
6 Technologieverkenning voor 6.1 Ajax . . . . . . . . . . . . 6.2 Adobe Apollo . . . . . . . 6.3 Adobe Flex 2 . . . . . . . 6.4 Conclusie . . . . . . . . .
58 de . . . . . . . .
webapplicatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . .
59 59 63 64 67
7 De 7.1 7.2 7.3
webservice Het webservice-concept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De JAX-WS-webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De AXIS-webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69 69 70 72
8 De 8.1 8.2 8.3
webapplicatie ontwikkeld met Flex Voorstelling applicatie . . . . . . . . . Programmastructuur . . . . . . . . . . Realisatie applicatie . . . . . . . . . .
81 81 82 99
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9 Besluit
101
A Een record uit de originele Access-databank
103
B Een voorlopige versie van het databankschema
105
C De DDL van de PostgreSql-databank
107
D De databankstructuur van de PostgreSql-databank
113
E De webservice - aanvullingen
115
Realisatie van de scriptie
121
iv Lijst van figuren
122
Bibliografie
124
Inleiding
Via de heer Schepens ben ik in 2006 terecht gekomen bij de heer Dupont die doctoreerde met een proefschrift over criminaliteit in de middeleeuwen. Hij maakte in het kader van dit doctoraat een databank aan met informatie over dit onderwerp. De structuur van deze databank is echter niet optimaal en kan enkel lokaal geraadpleegd worden. Het doel van deze thesis is in eerste instantie om de databank te converteren naar een exemplaar met meer structuur en dus ook meer mogelijkheden. In tweede instantie is het bedoeling om te exploreren wat de mogelijkheden zijn betreffende het publiceren van de databank via internet en de verwezenlijking hiervan. De thesis is opgedeeld in 3 grote luiken. Het eerste luik schetst de huidige situatie en schuift eventuele oplossingen naar voor. Het tweede luik omvat de normalisatie van de databank en het ontwikkelen van de hulpapplicaties hiervoor. Het laatste luik behandelt de ontwikkeling van de webapplicatie. De scriptie is verder als volgt ingedeeld: Deel 1, genaamd “Situatieschets”, bevat de volgende hoofdstukken: Hoofdstuk 1 bespreekt de huidige situatie van de databank en het gebruik ervan. Hoofdstuk 2 handelt over de mogelijkheden en doelstellingen die er zijn met betrekking tot het converteren en publiceren van de databank. Deel 2, met als titel “Conversie databank en bijbehorende hulpapplicaties”, bevat de volgende 3 hoofdstukken: Hoofdstuk 3 gaat in op de conversie van de bestaande databank naar een vernieuwd en beter gestructureerd exemplaar. Hoofdstuk 4 bekijkt de mogelijkheden die er zijn tot ontwikkeling van een programma voor het invoeren en wijzigen van data in de vernieuwde databank. v
vi Hoofdstuk 5 bespreekt de ontwikkeling van een applicatie die het mogelijk maakt om plaatsen uit de databank aan te duiden op een kaart. Deel 3, met als titel “De Webapplicatie” omvat de 3 volgende hoofdstukken: Hoofdstuk 6 handelt over de technologie¨en waarmee de webapplicatie zou kunnen ontwikkeld worden. Hoofdstuk 7 gaat in op de ontwikkeling van de webservice die de webapplicatie van informatie voorziet. Hoofdstuk 8 bespreekt de ontwikkeling van de webapplicatie met behulp van het Flex framework. Hoofdstuk 9 formuleert tenslotte het besluit van deze thesis.
Deel I
Situatieschets
1
HOOFDSTUK
1
Probleemsituering
De heer Guy Dupont doctoreert aan Universiteit Gent met de studie “Marginale groepen in Vlaanderen en Brabant 1385-1540”. In het kader van dit doctoraat is een databank aangemaakt met informatie over dit onderwerp. In deze databank vindt men vonnissen van misdrijven die gepleegd zijn in de opgegeven periode. Deze vonnissen kunnen handelen over valsspelen tot diefstal, waarbij de dader bijvoorbeeld bestraft wordt door geseling of verminking aan het oor. Deze informatie is het resultaat van jaren opzoekingswerk in de nog overblijvende stadsarchieven van toen. Alle gegevens zijn door de heer Dupont zelf ingevoerd en de databank telt nu ongeveer 16.000 records. In de databank is per gevonden vonnis van een verdachte een record aan de databank toegevoegd. Als er sprake is van meerdere verdachten van een misdrijf in een vonnis, dan zijn evenveel records toegevoegd als er verdachten zijn. Per vonnisrecord vindt men onder andere de persoonsgegevens van de dader, de persoonsgegevens van eventuele slachtoffers, de plaats, de aanklacht en de uiteindelijke straf. Om een duidelijker zicht op de inhoud van de databank te krijgen, is een record uit de databank als appendix toegevoegd (zie Appendix A). De databank is ontwikkeld via Microsoft Access en ook het toevoegen en raadplegen van de data gebeurt via datzelfde programma. Hiervoor is gebruik gemaakt van formulieren en is het mogelijk om de gewenste datavelden van de tabellen van de databank op het scherm te plaatsen door middel van tekstvelden. Aan ieder tekstveld is een veld uit een tabel verbonden. Op het formulier kunnen de tekstvelden op een overzichtelijke manier geschikt worden en verduidelijkt worden via labels. Via het formulier is het mogelijk om alle records van de databank te overlopen. Om te weten hoe een dergelijk formulier er precies uitziet kan het voorbeeldrecord in de reeds aangehaalde appendix A bekeken worden. De databank wordt 2
Hoofdstuk 1. Probleemsituering
3
door de heer Dupont ook gebruikt om statistische waarden van de aanwezige vonnissen te bepalen, wat mogelijk is met de formulierweergave van Access. Via Access is het enkel mogelijk om de databank lokaal te raadplegen. De structuur van de databank is echter niet optimaal. Achter het formulier dat gebruikt wordt voor het raadplegen en ingeven van de data, zit maar ´e´en tabel die 88 datavelden telt. Op deze manier is er dus erg veel herhaling. Zo handelen verschillende vonnissen dikwijls over dezelfde dader, maar toch wordt de persoonsinformatie van de dader in ieder vonnisrecord herhaald. Ook vinden we per record een gegevensveld voor plaatsen waar dezelfde dader nog toegeslagen heeft. Deze gegevens zijn echter geen verwijzingen maar gewoon strings van de betreffende plaatsnamen, van elkaar gescheiden door komma’s. Deze aanpak leidt onvermijdelijk tot foute informatie, een typfout is immers vlug gemaakt. Verder is er bij het invullen van de gegevensvelden dikwijls gebruik gemaakt van ’codes’ om aan te duiden dat de informatie niet geheel zeker is of onvolledig. Wanneer bijvoorbeeld de persoonsnaam van een slachtoffer begint met 2 liggende streepjes, mogelijk gevolgd door een beschrijving tussen haakjes, dan is deze naam niet bekend. Een bijkomend probleem is het feit dat de databank zoals eerder vermeld het werk is van meerdere jaren, maar door de jaren heen zijn de vele ‘truken’ waarvan juist een voorbeeld is gegeven niet hetzelfde gebleven. Naast de databank beschikt de heer Dupont ook nog over verschillende land- en stedenkaarten uit de gebieden waar de misdrijven gepleegd zijn in de opgegeven periode. Momenteel is er geen koppeling tussen deze kaarten enerzijds en de plaatsen uit de databank die men hierop zou kunnen aanduiden anderzijds. Het is duidelijk dat de situatie die hierboven is geschetst zeker niet optimaal te noemen is. Om de databank verder te kunnen ontwikkelen en meer informatie eruit te kunnen extraheren, dringt een conversie naar een exemplaar met een betere structuur en dus een hogere graad van normalisatie zich op. Deze databank bevat eveneens informatie die andere mensen van dienst kan zijn, daarom zou het interessant zijn om de databank via het internet toegankelijk te maken. In deze webapplicatie zouden de mensen de vonnissen kunnen opvragen en eventueel de betrokken plaatsen bekijken op een kaart. Dit verreist wel dat er een koppeling aangebracht wordt tussen de databank en de kaarten. In het volgende hoofdstuk worden deze doelstellingen verder uitgediept en wordt de haalbaarheid ervan besproken. De daaropvolgende hoofdstukken handelen over de verwezenlijking van deze doelstellingen.
HOOFDSTUK
2
Doelstellingen
In het vorige hoofdstuk is kort aangehaald welke applicaties zouden kunnen ontwikkeld worden. In de volgende sectie in gaan we in op de specifieke eisen voor deze applicaties. Deze doelstellingen waren echter nog niet geheel bekend bij aanvang van deze thesis, maar zijn tijdens het verloop ervan bepaald en verder uitgewerkt.
2.1 2.1.1
Specifieke eisen De conversieapplicatie
Deze applicatie moet de bestaande databank via een eenmalige conversie converteren in een beter gestructureerd exemplaar. Een nieuwe databank construeren met een hoge graad van normalisatie zal echter moeilijk zijn aangezien de bestaande databank te weinig structuur bezit. Aangezien de conversie eenmalig is en er dan vervolgens gewerkt wordt met het nieuwe exemplaar is een GUI voor dit programma niet vereist. De ontwikkeling van deze applicatie wordt besproken in hoofdstuk 3.
2.1.2
De administratieapplicatie
Via de administratieapplicatie moeten er nieuwe gegevens in de databank kunnen ingevoerd worden aangezien er nog geregeld nieuwe vonnissen gevonden worden. Ook moet het mogelijk zijn om de bestaande data in de databank te wijzigen. Deze applicatie moet ook toelaten dat er statistische acties op de data verricht worden en wordt besproken in hoofdstuk 4.
4
Hoofdstuk 2. Doelstellingen
2.1.3
5
De kaartenapplicatie
Het is de bedoeling dat deze applicatie extra functionaliteiten aan de administratieapplicatie toevoegt of de kaartenapplicatie kan misschien zelfs in de administratieapplicatie ge¨ıntegreerd worden. De extra functionaliteit van de kaartenapplicatie zorgt er voor dat de plaatsten uit de databank kunnen gekoppeld worden aan punten op de bestaande kaarten. Hiervoor is natuurlijk menselijk input verreist en het is de bedoeling dat de heer Dupont deze koppeling ingeeft. De ontwikkeling van de kaartenapplicatie wordt besproken in hoofdstuk 5
2.1.4
De webapplicatie
De webapplicatie moet het mogelijk maken dat ge¨ınteresseerden de informatie die te vinden is in de databank via het internet kunnen raadplegen. Het is mogelijk dat deze ge¨ınteresseerden zelf geschiedkundigen zijn en daarom moet bij elk gevonden vonnis de archiefbron vermeld worden. Zo kunnen deze geschiedkundigen zelf de desbetreffende bronnen opzoeken. De plaatsen uit de databank moeten zoals reeds aangegeven, kunnen aangeduid worden op de kaart. Met deze webapplicatie zijn er erg veel mogelijkheden, zo zou bijvoorbeeld het traject van een misdadiger “als een filmpje” op een kaart kunnen weergegeven worden. Indien het mogelijk is om deze informatie uit de databank te extraheren, zou het ook leuk zijn om bijvoorbeeld de migratiestromen van misdadigers in een bepaalde periode op een kaart te visualiseren. Deze migratiestromen hangen namelijk nauw samen met het al dan niet opfleuren van de economie van een bepaalde stad. Er moet natuurlijk nagegaan worden in hoeverre dit allemaal kan gerealiseerd worden en dit zal bepaald worden tijdens het verdere verloop van deze thesis. Hetgeen bij deze webapplicatie vooral niet uit het oog mag verloren worden is het feit dat de vonnissen nog altijd het belangrijkste zijn. Het koppelen van de data met de kaarten is enkel om het geheel op een visuele en dus attractievere manier voor te stellen. De ontwikkeling van de webapplicatie is het onderwerp van Deel II van deze scriptie.
2.2
De algemene structuur van de applicaties
Na het bepalen van de te gebruiken technologie¨en, wordt gekeken om de bestaande databank te converteren. Deze nieuwe databank zal dan gebruikt worden bij de ontwikkeling van de andere applicaties. Deze applicaties kunnen de databank op verschillende manieren benaderen. Voor de webapplicatie is het duidelijk dat dit via het internet zal gebeuren. Voor de administratieen de bijbehorende kaartenapplicatie zijn er meer keuzemogelijkheden. Deze mogelijkheden worden besproken in de onderstaande modellen.
2.2.1
Het gecentraliseerd model
In dit model wordt de databank door iedere applicatie via het internet benaderd. Dit heeft als voordeel dat wijzigingen aan de databank ook direct gelden voor de webapplicatie. Een
Hoofdstuk 2. Doelstellingen
6
bijkomend voordeel is dat wijzigingen makkelijk door verschillende personen kunnen gebeuren op verschillende locaties. Dit is echter geen vereiste aangezien wijzigingen enkel gebeuren door de heer Dupont. Met deze aanpak moet er wel gekeken worden voor een mechanisme dat ervoor zorgt dat eventuele simultane aanpassingen door verschillende personen aan de databank niet leiden tot inconsistenties of zelfs geheel onmogelijk zijn. Een bijkomend voordeel is dat er bij de ontwikkeling van de applicaties veel onderdelen kunnen hergebruikt worden. Het model is gevisualiseerd in figuur 2.1.
Figuur 2.1: Het gecentraliseerd model
Een nadeel aan dit model is het feit dat alle datatransfers over het internet gebeuren en dit kan leiden tot serieuze vertragingen. We mogen immers niet vergeten dat de databank 16.000 records bevat met een niet onaardig aantal datavelden waarbij verschillende datavelden veel tekst bevatten. Ook het verrichten van statistische acties wordt hierdoor bemoeilijkt.
2.2.2
Het gedecentraliseerd model
In dit model hebben we twee exemplaren van de databank, ´e´en exemplaar op de computer van de heer Dupont en ´e´en op de webserver. Alle wijzigingen gebeuren via de databank op de computer van de heer Dupont. Ook kan men vanaf deze databank de gegevens vlug raadplegen en er dus makkelijker statistische acties op uitvoeren aangezien er geen tussenkomst van het internet vereist is. De wijzigingen aan de databank zijn bijgevolg niet direct doorgevoerd naar de databank gebruikt bij de webapplicatie, dit gebeurt pas wanneer er een aantal wijzigingen zijn aangebracht. Deze situatie is geschetst in figuur 2.2
Hoofdstuk 2. Doelstellingen
7
Figuur 2.2: Het gedecentraliseerd model
Via deze werkwijze kan de databank enkel gewijzigd worden op de computer van de heer Dupont, maar zoals eerder aangehaald vormt dit niet echt een probleem. Ook het feit dat wijzigingen niet direct zullen gelden voor de webapplicatie vormt niet echt een probleem aangezien wijzigingen niet zo vaak voorkomen en het belang van de gegevens niet zo groot is dat de webapplicatie perfect up-to-date hoeft te zijn. Aangezien aan deze werkwijze grote voordelen verbonden zijn en de nadelen maar beperkt, wordt het project via dit model ontwikkeld.
2.3
Implementatietaal
Bij de keuze van de programmeertaal voor de applicaties wordt best gekeken voor een taal die kan gebruikt worden bij de administratie- en de kaartenapplicatie en ook bij het benaderen van de databank door de webapplicatie. Door deze aanpak is het mogelijk om veel methodes met betrekking tot de databanktoegang te hergebruiken.
2.4
Hosting webserver
Voor de webapplicatie is een webserver verreist. Hiervoor kan de “Amerigo”-server van Hogeschool Gent gebruikt worden. Dit is een Linux-server waarop het Java-platform ge¨ınstalleerd is. Als webcontainer is Apache Tomcat voorhanden.
Deel II
Conversie databank en ontwikkeling bijbehorende hulpapplicaties
8
HOOFDSTUK
3
Conversie databank
In dit hoofdstuk wordt de ontwikkeling van een conversieprogramma besproken. Deze applicatie is afhankelijk van de gekozen databank.
3.1
DBMS-keuze
Voor de nieuwe databank zal niet langer gebruik gemaakt worden van Microsoft Access. De webserver van Hogeschool Gent die ter onze beschikking staat voor het ontplooien van de webapplicatie is namelijk een Linux-server en Access kan enkel uitgevoerd worden op het Windows-besturingssysteem. Om diezelfde reden wordt ook niet gekozen voor Microsoft SQL Server. Een ander populair DBMS (Database Management System) die eventueel in aanmerking komt voor het realiseren van het project is Oracle. Maar deze DBMS is eigenlijk iets te geavanceerd en vraagt dan ook meer geheugen dan eigenlijk nodig is voor ons project. Twee andere populaire, ‘lichtere’ en enigszins vergelijkbare DBMS’en zijn MySQL en PostgreSQL, hoewel MySQL wel meer naambekendheid en een grotere ‘community’ heeft. Toch is gekozen voor PostgreSQL aangezien deze DBMS al beschikbaar is op de “Amerigo”-server van Hogeschool Gent (psqlODBC, 2007b). PostgreSQL is een ‘open source’ stabiele DBMS met relatief veel mogelijkheden die kan benaderd worden vanuit verschillende programmeertalen en ODBC.
3.2
Implementatietaal
De databank voor de webapplicatie zal door middel van Java benaderd worden, aangezien Java al ge¨ınstalleerd is op de “Amerigo”-server van Hogeschool Gent. Daarom lag de keuze
9
Hoofdstuk 3. Conversie databank
10
om de conversie- en administratie-applicatie in Java te realiseren ook voor de hand, vele modules voor het opvragen van data kunnen dan hergebruikt worden. Java kan perfect de PostgreSql- en de originele Access-databank benaderen via de daarvoor voorziene JDBCdrivers. De conversieapplicatie zal de oude databank geheel inlezen en de ingelezen data op een meer gestructureerde wijze in de nieuwe PostgreSql-databank stoppen. De databank inlezen via Java in combinatie met JDBC is niet echt supersnel te noemen (in vergelijking met het uitvoeren van SELECT/INSERT-statements via scripts rechtstreeks naar de databank toe bijvoorbeeld). Aangezien de databank toch een relatief groot aantal records bevat is het converteren een arbeidsintensieve job, die wat tijd kan vragen. Dit vormt echter geen probleem aangezien de conversie normaal gezien toch maar ´e´en keer hoeft uitgevoerd te worden. Na de conversie kan immers met de nieuwe databank verder gewerkt worden.
3.3
De structuur van de originele databank
De originele Access-databank bevat zoals eerder aangegeven 88 datavelden waarvan de meeste in het formulier zijn opgenomen. De velden waarbij dit niet is gebeurd, dateren meestal nog van in de beginperiode van de databank en zijn minder goed aangevuld. De precieze inhoud van het formulier en dus de databank is duidelijk weergegeven in het voorbeeldrecord dat in de Appendix A is toegevoegd. Voor de inhoud van dit hoofdstuk is ook gebruik gemaakt van het logboek over de inhoud van de databank, dat is opgesteld door de heer Dupont. De precieze inhoud van de databank wordt aan de hand van de gebieden of rubrieken in het formulier besproken.
3.3.1
De rubriek Misdrijf-Bron
In deze rubriek wordt het misdrijf beschreven en worden ook de archiefadressen vermeld van de bronnen waar het vonnisrecord is gevonden. De rubriek zoals die te zien is in het formulier, is weergegeven in figuur 3.1. Het veld ‘Id nr.’ is de oorspronkelijke autonummering van de records. Nadat in 2005 een bug in de databank vastgesteld werd, is er overgeschakeld op een nieuw veld autonr. Dit laatste veld is in tegenstelling tot het eerste wel correct ingevuld voor alle records. Voor de nieuwe databank is het enkel nodig om het veld autonr te behouden omdat het gebruikt wordt om vonnisrecords naar elkaar te laten verwijzen. De velden bron, stad, rubriek, archief, procedure en f◦ (folio) bepalen de archiefbronnen waar het vonnis is vermeld. Zoals in het voorbeeld te zien is, kan een vonnis in verschillende archiefbronnen vermeld staan. De verschillende archiefbronnen worden in de velden gescheiden door een puntkomma of een komma afhankelijk van het veld, maar deze manier van scheiden is echter niet altijd correct doorgevoerd. De velden jaar, datum en dag bepalen de tijd waarop het vonnis is geregistreerd. Het veld jaar is hier geen getal maar is als een tekst opgeslagen, hierdoor is het mogelijk om bijvoorbeeld “1506-1507” in te geven wanneer het precieze jaar niet geheel duidelijk is. We mogen immers niet vergeten dat de administratie van toen geheel anders te werk
Hoofdstuk 3. Conversie databank
11
Figuur 3.1: De Misdrijf-Bron-rubriek
ging dan nu en de datum in die tijd een veel minder grote rol speelde. In het veld ‘code(s)’ worden codes opgegeven die gekoppeld zijn aan misdrijven, de codes worden gescheiden door een liggend streepje. De letter in de code stelt de categorie van het misdrijf voor (bijvoorbeeld V=“vermogensdelicten”) en het daaropvolgende cijfer verwijst naar het specifieke soort misdrijf (bijvoorbeeld V02=“diefstal”). Een specifieke omschrijving van het misdrijf besproken in het vonnis is te vinden in het veld misdrijf en in het veld ‘aanklacht/vonnis’ wordt de aanklacht van het vonnis beschreven. Het veld ‘plaats misdrijf ’ bevat de locaties van de feiten die in het veld aanklacht/vonnis vermeld zijn. Deze plaatsen worden door komma’s gescheiden waarbij een plaats soms hi¨erarchisch verdeeld is in 2 niveaus (bijvoorbeeld “Gent (buurt van Korenmarkt)”). De velden excus’s en ‘vz code’ bevatten eventuele verzachtende omstandigheden. In het eerste veld worden verzachtende omstandigheden omschreven (bijvoorbeeld “blanco strafblad”) en in het tweede veld vindt men een corresponderende code hiervoor zoals bij het reeds besproken veld code misdrijf. De code “$” wordt gebruikt als er geen verzachtende omstandigheden opgegeven zijn. Deze codes worden door de heer Dupont onder meer gebruikt om statistische acties mee uit te voeren.
3.3.2
De rubriek Dader
In deze rubriek wordt informatie over een dader verschaft. De naam van de dader is meestal bekend, maar in de andere velden zoals het adres is meestal niets ingevuld. In figuur 3.2 is een voorbeeldrecord van een dader te zien. De meeste velden spreken voor zich. Het veld ‘verwant d’ (‘verwantschap d’) bevat gegevens over de relatie van de dader tot andere personen genoemd in de bronnen. Het Boolean-veld ‘Poorter d’ bepaalt of de verdachte als poorter van Brugge vermeld wordt en wanneer het Boolean-veld ‘nomm(´e)e?’ aangevinkt is, kent het gerecht de dader enkel via hun bijnaam. Het veld ‘namen mede-dd’ bevat de namen van mededaders
Hoofdstuk 3. Conversie databank
12
Figuur 3.2: De Dader -rubriek
gescheiden door komma’s, deze mededaders staan meestal ook in een ander record vermeld maar dan als dader. In het bovenstaande voorbeeld was blijkbaar geen precieze informatie over de mededaders bekend. De twee velden ‘tot # dd’ (totaal aantal daders) en ‘# bestrafte dd’ (aantal bestrafte daders) bepalen het aantal verdachten dat bij een misdrijf betrokken is en het aantal dat effectief vervolgd is. Wanneer het precieze aantal niet precies bekend is, maar enkel het minimumaantal, wordt dit bijvoorbeeld aangegeven door “05+”.
3.3.3
De rubriek Slachtoffer(s)
In figuur 3.3 zijn 2 voorbeeldrecords weergegeven van de rubriek Slachtoffer(s). In de velden
Figuur 3.3: De Slachtoffer(s)-rubriek
voor de naam wordt ofwel de desbetreffende naam opgegeven ofwel 2 streepjes indien de naam niet gekend is, eventueel gevolgd door een beschrijving tussen haakjes ofwel een opeenvolging
Hoofdstuk 3. Conversie databank
13
van namen gescheiden door schuine strepen. Het veld ‘vorm vn s’ (vorm voornaam slachtoffer) bevat de taalkundige vorm van de naam van het slachtoffer. Indien het meerdere slachtoffers betreft, worden de vormen van elkaar gescheiden door schuine strepen en de volgorde komt overeen met de volgorde van de opgegeven namen. “dim1m/vol1m/vol1v” staat bijvoorbeeld voor ‘een mannennaam in diminuitiefvorm’ gevolgd door ‘een mannennaam in de volle vorm’ en tenslotte ‘een vrouwennaam in de volle vorm’. Deze manier voor het opgeven van de vorm wordt soms ook op een licht gewijzigde manier toegepast, zo staat “geen2v” voor “twee vrouwen(namen) met onbekende voornaamsvorm”. Voor het opgeven van het geslacht wordt ook een code toegepast, zo betekent de code “vo2” dat er 1 vrouwelijk slachtoffer is en twee personen waarvan het geslacht onbekend is en de code “mv+” wordt gebruikt indien de slachtoffers bestaan uit 1 man en een onbekend aantal vrouwen. De betekenis van de overige velden spreekt voor zich.
3.3.4
De rubriek Straf
Deze rubriek bevat informatie over de uitgesproken straf (als die er is) en over het verloop van het proces zoals te zien is figuur 3.4.
Figuur 3.4: De Straf -rubriek
Voor het veld straf is terug een veld ‘str code’ (strafcode) voorzien die een opeenvolging van codes bevat die overeenkomen met het type straf. Het veld ‘uitvoer straf ’ is een verdere toelichting over de straf met informatie over de uitvoering ervan. Het veld plaats bevat de plaatsen waar het vonnis zich voltrokken heeft, dit veld bevat echter ook dikwijls informatie die hier niet thuishoort en in een ander veld zou moeten staan. De overige velden verschaffen informatie over de kost van het proces en de eventuele verbanning van de veroordeelde.
3.3.5
De rubrieken Terugkeer na verbanning, Varia en Statistiek
In figuur 3.5 worden de laatste 3 rubrieken van het formulier weergegeven. Indien er sprake is van een terugkeer na een verbanning zijn de 3 velden van de corresponderende rubriek ingevuld. In de velden ‘bio d’ (bio dader) en ‘bio s’ (bio slachtoffer) wordt de prosopografie of ‘persoonlijke geschiedenis’ van de persoon geschetst. Het veld checken wordt gebruikt om nog te verbeteren fouten in het desbetreffende vonnisrecord aan te geven. De 2
Hoofdstuk 3. Conversie databank
14
Figuur 3.5: De rubrieken Terugkeer na verbanning, Varia en Statistiek
velden in de rubriek Statistiek vergemakkelijken het zoeken naar vonnissen in een bepaalde periode en bepalen een vijf- of tienjaarlijkse periode waarin het vonnis zich volstrekt heeft. In de nieuwe databank is het echter de bedoeling om dit softwarematig te realiseren zodat deze 2 velden niet langer nodig zijn. Bij het bespreken van de datavelden zijn al verscheidene ‘truken’ besproken, via deze ‘truken’ is het mogelijk om alle benodigde informatie op te slaan in ´e´en tabel zonder relaties. Niet alle technieken zijn besproken in dit hoofdstuk, dit zou ons veel te ver leiden. Deze technieken kunnen heel eventueel gebruikt worden bij een erg kleine databank, maar wanneer de databank een omvang heeft zoals deze databank wordt de situatie moeilijk beheersbaar. Hetgeen na deze bespreking zeker duidelijk is geworden, is het feit dat de conversie naar een beter gestructureerd exemplaar, wat besproken wordt in het volgende hoofdstuk, zeker geen sinecure zal zijn.
3.4
De nieuwe databankstructuur
In dit deel van het hoofdstuk wordt de nieuwe databankstructuur besproken aan de hand van het nieuwe databankschema. Deze structuur is het resultaat van een proces dat bereikt is in verschillende stappen via intermediaire databankschema’s. Gezien de grootte van het schema is het echter onmogelijk om de verschillende versies in dit hoofdstuk te tonen, daarom is ervoor gekozen om enkel het laatste en dus finale databankschema weer te geven. In de Appendix B kan een intermediaire versie van het databankschema worden bekeken. Voor de structuur van de nieuwe databank moet in acht genomen worden dat de databank geconstrueerd wordt voor feiten uit de geschiedenis. Dit impliceert dat er geen zekerheid is over de juistheid van de data. Zoals we zullen zien bij de bespreking van de tabellen is er hierdoor geen perfect genormaliseerde structuur mogelijk. Indien we dit toch zouden proberen, zou er onvermijdelijk informatie verloren gaan. Zoals bleek bij de bespreking van de originele databank zijn er veel datavelden die bestaan uit meerdere waarden, waarbij iedere
Hoofdstuk 3. Conversie databank
15
waarde eigenlijk in een apart dataveld thuishoort. Bij het conversieproces wordt geprobeerd om deze waarden te scheiden en in aparte datavelden te stoppen, dit wordt echter bemoeilijkt door het feit dat voor eenzelfde dataveld niet overal dezelfde scheidingstekens gebruikt zijn. Ook de vele ‘truken’ en codes die gebruikt worden en veelal niet begrijpbaar zijn voor een computerprogramma, bemoeilijken het scheiden van de waarden.
3.4.1
De tabel Vonnis en bijbehorende tabellen
Bij de eerste versies van het databankschema is er voor het voorstellen van een vonnis gebruik gemaakt van de tabellen Misdrijf en Straf (zie Appendix B). Deze benadering was naar analogie met de gelijknamige rubrieken die gebruikt werden in het formulier. Deze tabellen zijn in volgende versies samengevoegd tot de tabel Vonnis omdat er tussen de twee voorgaande tabellen een ‘´e´en-op-´e´en-relatie’ bestond en ze ‘informatief’ gezien ook bij elkaar hoorden. Het databankschema is weergegeven in figuur 3.6 Voor de plaatsen vermeld in het Vonnis zoals misdrijfplaats en strafplaats wordt gebruik gemaakt van de tabel Plaats. De precieze inhoud en ontwikkeling van deze tabel komt aan bod in hoofdstuk 5. In dit hoofdstuk worden ook de tabellen nodig voor het koppelen van de plaatsen en de kaarten aan de tabel Plaats gekoppeld. De Plaats-records waarnaar verwezen wordt vanuit een Vonnis-record, kunnen ook gekoppeld zijn aan andere Vonnis-records. Daarom is een dataveld voor een eventuele opmerking voorzien wanneer verwezen wordt naar een Plaats-record. Hierdoor is het mogelijk om opmerkingen over de verwezen plaats, die enkel gelden voor het desbetreffende Vonnis-record, op te slaan. De betekenis van de velden in de Vonnis-tabel is analoog aan de betekenis van de ongeveer gelijknamige velden in het besproken formulier. De tabellen Procedure en VoorzieneStraf maken het mogelijk om het vonnis te categoriseren. Dit werd in de vorige databank gerealiseerd door in ieder record van de tabel in de desbetreffende velden ´e´en code te plaatsen. Met de nieuwe databank wordt aan deze code een verduidelijkende naam toegekend. De code moet ook in de databank aanwezig blijven voor statistische doeleinden, deze codes zijn enkel voor de heer Dupont nuttig en zullen dus niet getoond worden in de webapplicatie.
Hoofdstuk 3. Conversie databank
Figuur 3.6: De tabel Vonnis en bijbehorende tabellen
16
Hoofdstuk 3. Conversie databank
17
De tabellen VonnisSoortStraf, SoortMisdrijf en VonnisVzCode zorgen voor een gelijkaardige categorisering via codes zoals beschreven in de vorige paragraaf, maar er zijn meerdere codes per vonnis mogelijk. Deze codes zijn in de originele databank gescheiden door een bepaald scheidingsteken en moeten bij conversie van elkaar gescheiden worden. In de koppelingstabel VonnisSoortStraf kan additionele informatie over de soortstraf ingevuld worden via de daarvoor voorziene datavelden. Naast de codes voor de verzachtende omstandigheden die bepaald worden in de tabel VonnisVzCode, worden de verzachtende omstandigheden als tekst opgeslagen in het dataveld verzachtendeomstandigheden van de Vonnis-tabel. In de tabel Verbanning wordt informatie over de eventueel uitgesproken verbanning verschaft. Deze tabel heeft een ‘´e´en-op-´e´en-relatie’ met de Vonnis-tabel en de datavelden ervan zouden dus eigenlijk in de Vonnis-tabel kunnen geplaatst worden. Maar de velden van Verbanning-tabel zijn zelden ingevuld en daarom zorgt deze aparte tabel voor plaatsbesparing.
3.4.2
De toevoeging van de tabellen Persoon en Aanklacht
In figuur 3.7 wordt de tabel Vonnis hernomen, maar nu enkel met de datavelden die er nog moeten aan toegevoegd worden. Er zijn ook enkele tabellen voor het bijhouden van de personen en de aanklachten van het vonnis toegevoegd. Een groot probleem bij geschiedenisdatabanken blijkt de naamgeving van personen te zijn. In de middeleeuwen kon de naam van een persoon in de loop van zijn leven namelijk veranderen, als kind kon van de naam een verkleinwoord gemaakt worden. Het gerecht die de vonnissen opstelde, was ook niet altijd op de hoogte van de namen en gebruikte daarom soms enkel de bijnaam. In sommige vonnissen is als persoonsinformatie zelfs enkel het beroep van de dader bekend. In het ideale geval zou dezelfde persoon voorgesteld worden door ´e´en persoonsrecord, maar dit zal hier niet mogelijk zijn. Er kan nooit met volledige zekerheid gezegd worden of twee personen die op dezelfde manier beschreven worden in verschillende vonnissen wel degelijk dezelfde persoon zijn. Daarom wordt in ieder Vonnis-record verwezen naar twee Persoon-records, ´e´en voor het slachtoffer en ´e´en voor de dader. Aangezien daders dikwijls voorkomen in meerdere vonnissen, zullen evenveel Persoon-records worden aangemaakt over deze dader als het aantal vonnissen waarin hij genoemd wordt. In de Access-databank worden bij ´e´en vonnisrecord dikwijls verschillende slachtoffers opgegeven zoals besproken in het hoofdstuk 3.3.3. Voor de conversieapplicatie is het echter onmogelijk om de ‘multivalued’ datavelden, waarop verschillende ‘truken’ zijn toegepast, te scheiden en voor ieder slachtoffer een Persoon-record aan te maken. Via de tabel PersonenGelijk kan aangegeven worden dat twee Persoon-records over dezelfde persoon handelen. Aangezien dit nooit met volledige zekerheid kan gezegd worden, is er mogelijkheid om een zekerheidsfactor op te geven. Via de tabel BeroepPersoon kunnen meerdere beroepen voor een persoon opgegeven worden. In de originele databanktabel is er een veld aanklacht die verschillende ‘subaanklachten’ bevat die telkens beginnen met een cijfer. In de nieuwe tabel wordt dit veld gesplitst in een
Hoofdstuk 3. Conversie databank
18
Figuur 3.7: De tabel Persoon en Aanklacht
aanklachtinleiding en meerdere (sub)aanklachten. Het veld aanklachtinleiding is in de tabel Vonnis geplaatst en de ‘subaanklachten’ worden elk apart in de tabel Aanklacht gestopt waarbij ook een afzetplaats kan opgegeven worden. In de tabel MedeplichtigenNaam kunnen de namen van eventuele mededaders van een misdrijf of vonnis geplaatst worden, dit komt overeen met het veld ‘namen mede-dd’ in het formulier. De tabel MedeplichtigenPersoon heeft dezelfde functie als voorgaande tabel, maar nu kan in plaats van een naam verwezen worden naar een Persoon-record, wat natuurlijk een meer functionele oplossing is. In de tabel AanklachtSlachtoffers kunnen per ‘subaanklacht’ meerdere slachtoffers opgegeven worden. In de tabellen MedeplichtigenPersoon en Aanklachtslachtoffers wordt zoals in de tabel PersonenGelijk verwezen naar Persoon-records. Het is echter onmogelijk om deze informatie door de conversieapplicatie uit de originele databank te laten extraheren, deze tabellen kunnen enkel met behulp van de administratieapplicatie en dus menselijke interactie ingevuld worden. Deze laatste 3 tabellen worden bijgevolg ook maar effectief aan de databank toegevoegd wanneer
Hoofdstuk 3. Conversie databank
19
de administratieapplicatie klaar is.
3.4.3
De toevoeging van de archiefbronnen
Aan het databankschema zijn, zoals te zien in figuur 3.8, nog 3 nieuwe tabellen toegevoegd.
Figuur 3.8: De toevoeging van de archiefbronnen
Via de tabel VonnisArchief is het mogelijk meerdere archiefbronnen aan een vonnis te koppelen. Deze tabel bevat de datavelden archiefadres, folio en de verwijzingingen naar een Vonnisen een Rubriek -record. In de tabel Rubriek wordt vervolgens verwezen naar de tabel Bron waarin een archiefplaats kan opgegeven worden. Een rubriek kan opgevat worden als een hoofdstuk in een bron waarbij het veld folio vergelijkbaar is met een pagina in dat hoofdstuk. In de originele Access-databank werden de verschillende archiefadressen van een vonnis gescheiden door middel van een kommapunt en de overeenkomstige rubrieken, bronnen en folio’s door komma’s. Het probleem is echter dat deze scheidingstechniek niet overal in de databank correct is toegepast. Daarom wordt eerst geprobeerd om de verschillende archiefbronnen te splitsen en indien dit mogelijk is worden de desbetreffende bronnen apart in de nieuwe databank ingevoerd. In het andere geval wordt ´e´en archiefbron in de databank toegevoegd met de onveranderde en dus niet gesplitste waarden van de originele databank. De DDL (Data Description Language) voor het aanmaken van de tabellen is te vinden in Appendix C. Het volledige databankschema kan geraadpleegd worden in Appendix D.
3.5
Beschrijving applicatie
De applicatie leest zoals eerder vermeld de originele Access-databank in en schrijft de ingelezen records naar een nieuwe databank. Gezien het eenmalige karakter van de uitvoering van dit programma zal er geen GUI aan de applicatie gekoppeld worden. Wanneer de gebruiker de
Hoofdstuk 3. Conversie databank
20
applicatie uitvoert, wordt de conversie direct aangevat. Het programma wordt verwezenlijkt in Java, dus kan er gewerkt worden met een Properties-bestand. In dit bestand worden de parameters om de databanken te benaderen ingevuld. Wanneer er tijdens de uitvoering van de applicatie fouten optreden, worden de foutmeldingen naar een bestand geschreven. Eventueel kan het programma dan nog eens uitgevoerd worden nadat de oorzaak van de fout in de originele databank is verholpen. Voor het lokaliseren van de fout in de Access-databank wordt in de weggeschreven foutmelding onder meer het autonr meegegeven. (Voor meer informatie over het veld autonr, zie hoofdstuk 3.3.1) Bij de bespreking van de nieuwe structuur van de databank kwamen er dikwijls codes aan bod met een bijbehorende naam. Dit was onder meer het geval bij de tabel VoorzieneStraf, SoortMisdrijf en Procedure. Deze codes zijn gekozen door de heer Dupont en helpen met het indelen van de vonnissen in categorie¨en. Alle gebruikte codes worden aan de databank toegevoegd voor de gegevensconversie gestart wordt. Wanneer er vervolgens bij de conversie een code gevonden wordt in de originele databank die nog niet aan de nieuwe databank is toegevoegd, kan met zekerheid gezegd worden dat het een foutieve code betreft. Omdat er geen informatie verloren mag gaan, wordt de foutieve code toch in de desbetreffende tabel toegevoegd en wordt een foutmelding weggeschreven in het foutbestand. In de originele databank zijn ook velden aanwezig waar een opeenvolging van codes staat, zoals in het veld voor de codes van de verzachtende omstandigheden. Bij conversie wordt geprobeerd de codes te scheiden op basis van het gebruikte scheidingsteken en indien dit lukt, wordt iedere code apart in de nieuwe databank geplaatst. In het andere geval, wordt het volledige veld zonder te splitsen ingevoegd in de databank en wordt een foutmelding gegenereerd.
3.6
Het benaderen van de databank
De conversieapplicatie benadert de beide databanken door middel van de Java-Prepared Statements. Hiermee kunnen SQL-queries voorgecompileerd worden en kan de query vervolgens meerdere keren effici¨ent gebruikt worden. Bij iedere individuele query worden de parameters ingesteld via de daarvoor voorziene methodes. De Access-databank wordt per record ingelezen en na het inlezen van een record wordt de data naar de tabellen van de PostgreSqldatabank geschreven. Tijdens een eerste fase in de ontwikkeling van de applicatie werden de ‘multivalued’ datavelden nog niet opgesplitst. In deze fase werd gebruik gemaakt van een vergelijkingsmethode die de data van de Access-databank vergeleek met de weggeschreven data van de PostgreSql-databank om te testen of beide overeenkwamen. In een volgende fase werden de ‘multivalued’ velden gescheiden. Het is opmerkelijk dat een Boolean-waarde of een zogenaamd ‘Ja/nee’-veld bij de Accessdatabank geen Null -waarde kan bevatten. Hierdoor staat de waarde false in de databank voor zowel ’waar’ als ’geen informatie over beschikbaar’. Bijgevolg zijn ook alle ingelezen
Hoofdstuk 3. Conversie databank
21
Boolean-waarden in de PostgreSql-databank gelijk aan false of true en nooit aan Null. In de PostgreSql-databank was aanvankelijk een probleem met het weergeven van woorden die speciale tekens bevatten zoals de franse ‘´e’. De oorzaak hiervan is de standaardinstelling van PostgreSql die de databank op ‘SQL-ASCII’-codering zette en deze codering gebruikt slechts 7 bits. Het probleem werd opgelost door de codering op ‘UTF-8’ in te stellen. Wanneer de DDL van de databank ingeladen wordt via de ‘Command Prompt’ van Windows, lukt het echter nog altijd niet om speciale tekens zoals ‘´e’ toe te voegen. Maar dit is blijkbaar te wijten aan de ‘Command Prompt’ aangezien er via de administratieapplicatie van PostgreSql, ‘pgAdmin’, geen problemen zijn bij het invoeren. Via de administratie-, kaarten- en webapplicatie moeten gerichte opzoekingen in de PostgreSql-databank mogelijk zijn. Records in de databank moeten opgezocht kunnen worden via het opgeven van een deelstring voor het desbetreffende dataveld. Dit kan in PostgreSql verwezenlijkt worden door voor en na de deelstring een ‘%’-teken te plaatsen in combinatie met een LIKE -operator bij het uitvoeren van de zoekopdracht. Hiermee vindt men alle velden die de deelstring bevatten. Via PostgreSql is het ook mogelijk om ‘case-insensitive’ via deelstrings te zoeken met de ‘˜ *’-operator. Met volgende zoekopdracht worden bijvoorbeeld alle plaatsen opgehaald die het woord ‘brugge’ in hoofd- en/of kleine letters bevatten. select * from plaats where naam ~* ’brugge’;
3.7
De functionaliteit van de applicatie
Het is niet de bedoeling dat de conversieapplicatie op de computer van de heer Dupont ge¨ınstalleerd wordt. Voor hem is enkel het resultaat, zijnde de nieuwe PostgreSql-databank van belang. Het converteren van de databank wordt dus voor de heer Dupont gedaan en vervolgens kan de nieuwe databank gekopieerd worden naar zijn computer. Voor het kopi¨eren van de databank “criminaliteit” kan de volgende opdracht gebruikt worden. $ pg_dump criminaliteit > db.out Het tekstbestand “db.out” bevat na uitvoering van het commando de SQL-commando’s nodig om de databank opnieuw te reconstrueren. Om de databank vervolgens in te laden is de volgende opdracht beschikbaar: $ psql -d criminaliteit -f db.out Het is via deze werkwijze zelfs mogelijk om het back-uppen en inladen van de database in een ander besturingssysteem uit te voeren.
HOOFDSTUK
4
De administratieapplicatie
4.1
De huidige situatie
De originele databank is ontwikkeld via Microsoft Access 2003. De invoering en manipulatie van de gegevens in deze databank gebeurt via formulieren zoals beschreven in hoofdstuk 1. In de appendix (zie Appendix A ) is een voorbeeld toegevoegd die toont hoe een vonnisrecord weergegeven wordt in het formulier dat gekoppeld is aan de databanktabel. Voor de statistische acties is een dergelijk formulier erg geschikt, hiervoor kan de “Filter-functie” gebruikt worden. Zo is het mogelijk om aan enkele tekstvelden (die verbonden zijn aan een dataveld uit de databanktabel) een filter te koppelen. Na het uitvoeren van deze filter kan men alle records die beantwoorden aan de opgegeven filter in de formulierweergave overlopen. Voor het opgeven van de filter zijn er vele mogelijkheden, zo kunnen alle records opgevraagd worden waarvan een dataveld een bepaald woord bevat of gelijk is aan een ander dataveld.
4.2
De applicatie in Java
Aangezien de conversieapplicatie en de webapplicatie ge¨ımplementeerd zijn in Java, ligt de keuze om voor deze applicatie ook Java te gebruiken voor de hand. Via JDBC kan de PostgreSql-databank zoals gezien in het vorige hoofdstuk perfect benaderd worden. Het ontwikkelen van de applicatie voor een dergelijke uitgebreide databank die het mogelijk moet maken om de data te raadplegen en in te voeren ‘from scratch’ is echter zeer tijdrovend. Ook het feit dat via deze applicatie statistische acties op de data moeten kunnen uitgevoerd worden zoals in de besproken formulierweergave van Access vraagt veel tijd. Daarom is in overleg met de interne promotor beslist om geen administratieapplicatie ‘from scratch’ te
22
Hoofdstuk 4. De administratieapplicatie
23
ontwikkelen aangezien dit, gezien de beperkte tijdspanne, onmogelijk te combineren valt met de andere te ontwikkelen applicaties in deze thesis. Eventueel kan wel gekeken worden of er geen bestaande programma’s zijn die mits enige aanpassing de PostgreSql-databank kunnen benaderen en instaan voor de data-invoering en -manipulatie. Hiervoor is in eerste instantie gezocht naar ‘open source’ programma’s die hiervoor geschikt zijn, maar er is geen enkel project gevonden dat hiervoor in aanmerking kwam. Vervolgens is uitgezocht of de formulierweergave van Access niet in aanmerking zou kunnen komen voor de verwezenlijking van de administratieapplicatie.
4.3
De applicatie via formulierweergave
De formulierweergave van Access biedt zoals reeds aangegeven enkele interessante functionaliteiten, daarom zou het handig zijn om deze formulieren te gebruiken in combinatie met de nieuwe database. De formulieren worden dan niet langer aan ´e´en Accesstabel gekoppeld, maar aan de nieuwe aangemaakte PostgreSql-tabellen. Dit kan gerealiseerd via een ODBC-brug. De formulieren worden in dit geval gebruikt als ‘frontend’ voor de PostgreSql-databank, die dan als ‘backend’ optreedt. Voor de ODBC-brug wordt de “psqlODBC”-driver ge¨ınstalleerd, dit is een offici¨ele driver voor het benaderen van een PostgreSql-databank via ODBC (Open Database Connectivity) (psqlODBC, 2007a). Na het downloaden van deze driver, kunnen de desbetreffende instellingen volgens het helpbestand worden ingesteld via een nieuw toegevoegd item in het Control Panel van Windows. Na enig zoekwerk bleek dit item echter in de Administrative Tools-map van het All Programs-Windows-menu geplaatst te zijn. Voor het beschikbaar stellen van de databank via ODBC hebben we de keuze tussen “User DSN”, “System DSN” en “File DSN”. ‘DSN’ staat hier voor “Data Source Name”. Het enige verschil tussen “System DSN” en “User DSN” is wie allemaal toegang heeft tot de ‘data source’. We gaan gebruik maken van “File DSN”, hoewel dit eigenlijk niet echt een ‘data source’ is. Een “File DSN” is enkel een bestand dat alle connectieparameters bevat voor een directe connectie naar de ODBC-driver. We zullen werken met de recentste versie van Microsoft Access 2007, die enkele nieuwigheden bevat ten opzichte van de 2003-versie. De meeste nieuwigheden richten zich echter op de interface van het programma. Een nieuwigheid die voor ons interessant zou kunnen zijn is de mogelijkheid om in de formulierweergave gebruik te maken van een gesplitst formulier. Hierbij wordt de gekende formulierweergave gecombineerd met gegevensbladweergave. Er kan ook gebruik gemaakt worden van een subformulier, dit is een formulier dat ingevoegd is in een ander formulier. Subformulieren kunnen gebruikt worden om gegevens te tonen uit tabellen met een een-op-veel-relatie. In Access wordt een koppeling naar de nieuwe aangemaakte ODBC-‘data source’ aangemaakt
Hoofdstuk 4. De administratieapplicatie
24
en vervolgens kunnen de tabellen van PostgreSql-databank ge¨ımporteerd worden. Aangezien we gebruik hebben gemaakt van een ‘File DSN’ worden wijzigingen aangebracht in de ge¨ımporteerde tabellen ook doorgevoerd naar de ‘backend’ PostgreSql-databank. Aan deze ge¨ımporteerde tabellen kan nu een formulier gekoppeld worden. Er is echter een fundamenteel probleem bij het invoeren en wijzigen van data via de formulierweergave. Stel dat er een Vonnis-record getoond wordt samen met de bijbehorende misdrijfplaats. In het formulier worden dan onder meer verwijzingen opgenomen naar de datavelden van de tabel Vonnis en de datavelden van Plaats. Wanneer in het formulier een Vonnis-record getoond wordt, dan wordt ook de bijbehorende misdrijfplaats weergegeven. Het Plaats-record dat getoond wordt als misdrijfplaats is bepaald door het misdrijfplaatsid van het Vonnis-record dat getoond wordt. Wanneer een gebruiker dat wenst, kan het Plaatsrecord dat weergegeven wordt bij het Vonnis-record, gewijzigd worden. Een probleem dat hierbij optreedt, is het feit dat dit Plaats-record ook kan gekoppeld zijn aan andere records in de tabellen. Hierdoor zou het desbetreffende Plaats-record ook wijzigen voor de andere records in de tabellen. De gebruiker zou dus de keuze moeten hebben om het Plaats-record te wijzigen (en dus ook voor alle daaraan gekoppelde records) of een nieuw Plaats-record aan te maken enkel voor het desbetreffende Vonnis-record. Dit blijkt helaas niet mogelijk te zijn met de formulierweergave in Microsoft Access en daarom is de ontwikkeling van de administratieapplicatie stopgezet.
HOOFDSTUK
5
De kaartenapplicatie
5.1
Inleiding
Via de webapplicatie is het mogelijk om plaatsen grafisch weer te geven op een kaart. Enerzijds hebben we de plaatsen die in de databank kunnen worden gevonden in de al bestaande tabel Plaatsen. Deze plaats kan fungeren als strafplaats, geboorteplaats, afzetplaats of archiefplaats. Anderzijds zijn er kaarten van de gebieden uit de periode van de misdrijven ter beschikking gesteld door de heer Dupont. Er is dus nog nood aan een applicatie die via menselijke input de link tussen het punt op de kaart en de plaats in de databank kan leggen. Deze applicatie kan dan door de heer Dupont zelf gebruikt worden. Indien er verder in dit hoofdstuk vermelding wordt gemaakt van de benaming ’plaats’, dan wordt er een plaats uit de databank bedoelt. Met een ’punt’ wordt een punt of co¨ordinaat op de kaart bedoeld. Eenzelfde plaats uit de databank moet kunnen gelocaliseerd worden op verschillende kaarten en de precieze locatie van deze plaats op de kaarten zal hoogstwaarschijnlijk telkens verschillend zijn. Via de applicatie moet het dus mogelijk zijn om deze koppeling in toe te voegen, te wijzigen of te verwijderen. Verder moet het mogelijk zijn om een type in te stellen en moet ook ingesteld kunnen worden tot welk gebied een plaats behoort. De kaartenapplicatie zal gebruikt worden naast de administratieapplicatie, die zorgt voor het beheren van alle data in de PostgreSql-databank. Bijgevolg moet er voor gezorgd worden dat de kaartenapplicatie de databank op dezelfde manier als de toekomstige administratieapplicatie benadert. Deze zal, zoals eerder aangegeven, een zogenaamde standalone-applicatie zijn die haar informatie niet over het internet haalt. Hiermee moet dus ook rekening gehouden worden bij het ontwikkelen van de kaartenapplicatie. 25
Hoofdstuk 5. De kaartenapplicatie
5.2
26
Gebruikte technologie
Het lag voor de hand om deze applicatie in JAVA te implementeren, aangezien de JDBCdatabanklaag van het conversieprogramma gedeeltelijk kan hergebruikt worden. Ook biedt JAVA veel mogelijkheden voor het tekenen op een kaart. De JDBC-databanklaag van de kaartenapplicatie benadert in deze applicatie de reeds bestaande PostgreSql-databank. Voor de ontwikkeling van het programma wordt teruggegrepen naar het bij Java geleverde NetBeans.
5.3
Het aanvullende databankschema voor kaarten
De in dit hoofdstuk behandelde databankschema’s zijn een uitbreiding van het eerder besproken databankschema dat gebruikt wordt bij de conversie van de originele databank (zie hoofdstuk 3.4 ). Aan het originele databankontwerp worden enkele tabellen toegevoegd en in de tabel Plaats, die reeds aanwezig was in het originele databankontwerp, worden enkele verwijzingen naar de nieuw gemaakte tabellen toegevoegd. In de volgende paragrafen wordt in verschillende stappen beschreven hoe het uiteindelijke aanvullende databankschema voor kaarten tot stand kwam.
5.3.1
Aanvullend databankschema versie 1
Zoals men op de volgende figuur kan zien is de tabel Plaats uit het vorige basisdatabankschema hernomen. Men kan een plaats laten verwijzen naar een stad en er kan ook een streek aan een plaats gekoppeld worden. Via de tabellen PlaatsKaart en Kaart is het mogelijk om een plaats op verschillende te kaarten weer te geven via de velden coordinaatx en coordinaaty. Aan een plaats kan men ook per periode, gespecificeerd door beginjaar en eindjaar, een land koppelen. In deze versie is ervoor gekozen om van het veld stadid in de tabel Plaats geen Primary Key te maken aangezien het niet zeker is dat er een stad horende bij de plaats bekend is. (zie figuur 5.1) In deze versie van het databankschema is het enkel mogelijk om een plaats uit de databank op een kaart weer te geven. Een stad en een land (eventueel met een periode) kunnen niet op de kaart weergegeven worden. De keuze om een plaats tot een land te laten behoren per periode is eigenlijk ook niet echt nodig. In de periode waarover de databank handelt zijn er namelijk amper veranderingen aan de landenstructuur. Ook is er in deze periode ook niet echt sprake van landen en worden gebieden op een andere manier ingedeeld. Het veld streek wordt ook in ieder record van Plaats herhaald, wat dus onvermijdelijk tot herhaling zal leiden.
Hoofdstuk 5. De kaartenapplicatie
27
Figuur 5.1: Aanvullend databankschema versie 1
5.3.2
Aanvullend databankschema versie 2
In deze versie is het eerder vermelde probleem met de landen opgelost door een land op dezelfde manier te behandelen als een streek. Ook is er een tabel Straat toegevoegd waarnaar kan verwezen worden uit de tabel Plaats. Hierdoor kan een plaats nu ook tot een straat behoren. In de tabel Straat kan ook verwezen naar de stad waartoe een straat behoort. Een stad kan dan op zijn beurt behoren tot verschillende streken via de tabel StadStreek. Een streek kan ook behoren tot een grotere streek via de tabel StreekStreek (De streek “Vlaanderen” behoorde bijvoorbeeld tot de bovenliggende streek “De Nederlanden”). In deze laatste tabel wordt dus voor iedere streek een ouderstreek opgegeven. Aan de tabel Plaats zijn de velden beschrijving en nr toegevoegd. In het veld beschrijving kan eventuele randinformatie gegeven worden over de plaats en in het veld nr kan het huisnummer van de plaats ingevuld worden. Ook de straten-, steden- en strekenrecords kunnen op verschillende kaarten geplaatst worden door respectievelijk de tabellen StraatKaart, StadKaart en StreekKaart zoals bij de koppeling tussen de plaatsen en de kaarten. Maar bij deze tabellen wordt er echter niet direct een coordinaatx en coordinaaty per record geplaatst zoals in de tabel PlaatsKaart. De co¨ordinaten worden nu per combinatie van een straat, streek of stad en een kaart verbonden met de tabellen respectievelijk CoordinatentabelStraat, CoordinatentabelStreek en CoordinatentabelGebied. Hierdoor is het mogelijk om deze objecten door meerdere punten op een kaart voor te stellen. In het geval van een straat kan men bijvoorbeeld de straat bepalen door een opeenvolging van punten op de kaart. (zie figuur 5.2)
Hoofdstuk 5. De kaartenapplicatie
Figuur 5.2: Aanvullend databankschema versie 2
28
Hoofdstuk 5. De kaartenapplicatie
29
Een nadeel van deze benadering is dat men niet kan aangeven over welk type streek het betreft. Ook zou een stad kunnen gezien worden als een soort van streek, maar het verschil tussen de twee zou dan wel aangegeven moeten kunnen worden.
5.3.3
Aanvullend databankschema versie 3
In versie 3 van dit databankschema is de tabel Streek vervangen door de tabel Gebied. De tabel Stad is verwijderd, de steden worden nu ook geplaatst in de tabel Gebied. Aan deze tabel is nu een type verbonden, hierdoor kan men specificeren of het gebied bijvoorbeeld een gehucht, stad of vorstendom is. De overige tabellen zijn gelijkaardig aan het vorige databankschema, enkel werd aan de tabel GebiedKaart nog een centrumco¨ordinaat toegevoegd die het centrum van een gebied bepaald. In de tabel GebiedGebied kan men voor een gebied een oudergebied (of bovenliggend gebied) opgeven waarbij men kan eisen dat het type van het oudergebied hoger moet zijn dan het gebied zelf. Hierdoor is het dan bijvoorbeeld onmogelijk om het vorstendom “Vlaanderen” te laten behoren tot de stad “Veurne” (met een lager type).
Figuur 5.3: Aanvullend databankschema versie 3
Hoofdstuk 5. De kaartenapplicatie
30
Deze versie is al goed werkbaar, maar een straat wordt op een andere manier behandeld dan een gebied. Nochtans kan een straat ook als een soort gebied gezien worden. Enkel de manier van voorstellen op een kaart is anders, aangezien een straat voorgesteld wordt door een lijn van punten. Door een gebied en een straat zoals nu op een andere manier te behandelen, wordt het wijzigen van data in de databank bemoeilijkt. In de kaartenapplicatie moeten hiervoor immers speciale methodes voorzien worden. Dit wordt opgelost in het volgende databankschema.
5.3.4
Aanvullend databankschema versie 4
Deze finale versie van het schema lost het probleem van het onderscheid tussen straten en gebieden op en gaat zelfs nog een stap verder. De gebieden en straten van de vorige versie worden nu beide vervangen door een plaats. Ze worden dus op dezelfde manier behandeld als de al bestaande plaatsen in de databank. Dit heeft als voordeel dat gebieden, straten en plaatsen op dezelfde manier kunnen benaderd worden. Het nadeel is wel dat er geen onderscheid meer is tussen de de straten en gebieden die zijn ingebracht via de kaartenapplicatie en de originele plaatsen in de oorspronkelijke databank. Het enige wat in de vorige versie van het aanvullende databankschema kon veranderd worden via de kaartenapplicatie waren de verwijzingen naar straten en gebieden in de tabel Plaats en de nieuw aangemaakte tabellen. Hierdoor was er een scheiding tussen het originele databankschema van bij de conversie en het nieuwe aanvullende databankschema voor de kaarten. Maar dit nadeel weegt niet op tegen het voordeel van de uniforme behandeling voor straten, gebieden en plaatsen. De originele plaatsen krijgen nu bij conversie het type “Onbepaald”(0), maar dit kan later door de kaartenapplicatie gewijzigd worden. Naast het nieuwe veld voor het aangeven van het type in de tabel Plaats, wordt aan deze tabel ook het voor zichzelf sprekende veld actuelenaam toegevoegd. (zie figuur 5.4)
Figuur 5.4: Aanvullend databankschema versie 4
Hoofdstuk 5. De kaartenapplicatie
31
De mogelijke typen voor een plaats worden hieronder gegeven en zijn bepaald door de heer Dupont. • Onbepaald (0) • Gebouw (1) • Straat (2) • Wijk/Parochie (3) • Dorp/Stad (4) • Kasselrij (5) • Vorstendom (6) Zoals eerder gezegd is het standaardtype bij conversie “Onbepaald”. Het lijkt misschien raar om een dorp en een stad als hetzelfde type te behandelen, maar omstreeks 1450 kende men niet de hedendaagse indeling van dorpen en/of deelgemeentes die behoren tot een stad of een gemeente. 5.3.4.1
De hi¨ erarchie van de plaatsen.
Via de tabel PlaatsPlaats kan men aan iedere plaats een ouderplaats toekennen. Dit wordt grafisch via een voorbeeld voorgesteld in figuur 5.5
Figuur 5.5: Een voorbeeldhi¨erarchie van plaatsen
Hoofdstuk 5. De kaartenapplicatie
32
In dit voorbeeld heeft de kasselrij “Brugge” als ouderplaats het vorstendom “Vlaanderen”. Wanneer men deze hi¨erarchie van plaatsen overloopt volgens de relatie “heeft als ouderplaats” of de relatie “heeft als kinderplaats” (de lijnen worden dus vervangen door pijlen naar boven of respectievelijk naar beneden) bekomt men een “Directed Acyclic Graph” of “DAG”. Deze vorm van graaf of boom heeft geen lussen en is dus ook relatief makkelijk te overlopen. Via de kaartenapplicatie kan maar ´e´en directe ouderplaats voor een plaats opgegeven worden en dit vergemakkelijkt sterk het opvragen van data over deze hi¨erarchie van plaatsen. In bovenstaand voorbeeld moet bij de plaats “Sint-Michiels” via de kaartenapplicatie als directe ouderplaats “Brugge” opgegeven worden. Hierdoor wordt de plaats “Vlaanderen” echter ook direct een ouderplaats van “Sint-Michiels”. Deze boomstructuur moet dus vertolkt worden in het relationele databankmodel via de tabel PlaatsPlaats. In deze tabel wordt er in elke plaats verwezen naar het ouderrecord, deze manier om hi¨erarchische informatie voor te stellen wordt het adjacency-model genoemd. Om dit model uit te leggen wordt er voorlopig met een vereenvoudigde tabel plaatsen gewerkt waarvan de inhoud hieronder weergeven is (en met vorstendom als hoogste type- of levelnummer). In deze tabel wordt via de kolom ouder verwezen naar de unieke key van het onmiddellijke ouderrecord. naam | ouder | level | key -----------------+-------+-------+--------Vlaanderen | VL | 0 | VL Brugge | VL | 1 | VL.BR Kortrijk | VL | 1 | VL.KT Sint-Michiels | VL.BR | 2 | VL.BR.SM Sint-Kruis | VL.BR | 2 | VL.BR.SK Baggaertsstraat | VL.KT | 3 | VL.KT.BA Indien we nu alle kindplaatsen van Vlaanderen willen opvragen aan de databank moeten we dit via een recursieve SQL-query oplossen. De benodigde records zijn immers verspreid over verschillende niveaus in de boom. Om de query op te stellen maakt men gebruik van een Common Table Expression of CTE. Deze CTE wordt gedefinieerd door middel van een WITH -opdracht gecombineerd met een hoofdquery. (Moreau, 2007). De CTE kan men vergelijken met een inline view of tijdelijke tabel. Deze tabel wordt niet permanent in de databank opgeslagen, maar wordt enkel in het geheugen opslagen tot de uitvoering van de hoofdquery stopt. Voor het opvragen van hi¨erarchische informatie moet de CTE recursief zijn. Daarom verwijst de CTE naar zichzelf en wordt deze gedefineerd als een samengestelde SELECT opdracht met een union-operator en minstens 2 deelopdrachten. De eerste deelopdracht is het ankerlid en wordt slechts een keer uitgevoerd. De tweede deelopdracht is het recursieve lid dat wordt uitgevoerd tot er geen nieuwe rijen meer kunnen gegenereerd worden. In het volgende voorbeeld dat de kindplaatsen van “Vlaanderen” opvraagt, wordt de werkwijze
Hoofdstuk 5. De kaartenapplicatie
33
ge¨ıllustreerd. Het voorbeeld is overgenomen uit de cursus van de heer Moreau (Moreau, 2007) en aangepast aan onze tabel. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
WITH x AS (SELECT naam kind, CAST(naam AS NVARCHAR(255)) kinderen, key, 0 level FROM [plaatsen] WHERE key=’VL’ UNION ALL SELECT b.naam, CAST(x.kinderen + ’,’ + b.naam as NVARCHAR(255))), b.ouder, x.level + 1 FROM [plaatsen] b , x WHERE b.ouder = x.key ) SELECT REPLICATE(’|---’,level) + kind FROM x ORDER BY kinderen
Na uitvoering van deze query bekomen we volgend resultaat: Vlaanderen |---Brugge |---|---Sint-Michiels |---|---Sint-Kruis |---Kortrijk |---|---|---Baggaertsstraat De WITH -opdracht declareert een tijdelijke tabel x die wordt gebruikt in de hoofdquery (regel 12). Deze opdracht wordt voor de hoofdquery uitgevoerd en levert de tabel x. In deze WITH -opdracht staat het ankerlid op regel 1. Het ankerlid wordt ´e´en keer uitgevoerd en zoekt de plaats “Vlaanderen” op. Het recursieve lid dat start op regel 7 wordt verschillende malen herhaald. Dat lid is recursief aangezien er verwezen wordt naar de eigen tabel x van de WITH-opdracht. Bij elke iteratie wordt door de CTE een rij aangemaakt voor elk kind van de plaats gevonden bij de vorige iteratie. Met de 2 CAST-opdrachten wordt voor iedere behandelde plaats het pad van de hi¨erarchie bijgehouden. Dit wordt later in de hoofdquery enkel gebruikt om de plaatsen te kunnen rangschikken. Het opvragen van de ouderplaatsen voor een bepaalde plaats gebeurt op dezelfde manier via CTE’s. Via deze methode is het dus mogelijk om op een performante manier informatie over de hi¨erarchie van plaatsen op te vragen. Ook de Oracle-databank heeft hiervoor een implementatie voorzien. Weliswaar wordt dan geen gebruik gemaakt van de WITH -opdracht,
Hoofdstuk 5. De kaartenapplicatie
34
maar wordt gewerkt met de opdrachten start with en connect by. Het probleem is echter dat de databank PostgreSql geen recursiviteit ondersteunt. (Documentation Postgres, 2007) (Postgres Forum, 2007). Op het internet is hiervoor een patch te vinden die de WITH opdracht toevoegt. Toch kiezen we ervoor om deze niet te gebruiken omdat het een onoffici¨ele uitgave is en de functionaliteit ervan beperkt. (Evgen Potemkin, 2007) In deze versie van de databankstructuur is ervoor gekozen om een verwijzing naar ieder ouderrecord van een bepaalde plaats op te slaan. In het voorbeeld ge¨ıllustreerd in figuur 5.5 staat “Sint-Kruis” dus tweemaal in de tabel PlaatsPlaats: eenmaal met als ouderplaats “Brugge” en nog eens met “Vlaanderen” als ouderplaats. Hoewel deze werkwijze niet echt volgens het relationele databankmodel is, we slaan immers redundante informatie op, is het toch een aanvaardbare keuze. De plaats die hiervoor in de databank vereist is, blijft relatief beperkt aangezien in de tabel PlaatsPlaats enkel gewerkt wordt met verwijzigen naar de desbetreffende plaatsrecords. Ook kan een plaats maar zes keer met een bepaalde ouderplaats in deze tabel staan. Het type van iedere ouderplaats moet immers hoger zijn dan de plaats zelf en er zijn maar zes types die als ouderplaats kunnen fungeren. Daarbij is dit het slechts denkbare geval, gemiddeld hebben we veel minder plaats nodig. Voor de webapplicatie moeten we dikwijls de ouderplaatsen of kindplaatsen van een bepaalde plaats opvragen en via de besproken aanpak is dit mogelijk via ´e´en databank-query. Het invoegen, wijzigen en verwijderen van relaties in de plaatsenhi¨erarchie vraagt nu echter wel veel verschillende databanktoegangen. Deze acties worden echter enkel via de kaartapplicatie uitgevoerd en zijn in vergelijking met het louter opvragen van de informatie uiterst zeldzaam. Het invoegen van een relatie gebeurt normaal gezien slechts eenmaal en het wijzigen en verwijderen van een relatie gebeurt enkel in geval van een fout.
5.4
De kaartenapplicatie versie 1
Deze eerste versie van de applicatie is ontwikkeld om te werken met de derde versie van het aanvullende databankschema voor kaarten (zie sectie 5.3.3). Via deze versie van de kaartenapplicatie kunnen straten en gebieden aan de databank toegevoegd en verwijderd worden en kan een plaats uit de geconverteerde databank gekoppeld worden aan een straat of een gebied. Er kunnen kaarten toegevoegd en verwijderd worden uit de databank en plaatsen, straten of gebieden kunnen gekoppeld worden aan verschillende kaarten. De invoeg-, wijzig-, verwijder- en koppelbewerkingen gebeuren allemaal via de knoppen die in het linkerpaneel staan. Wanneer er geklikt wordt met de linkermuisknop op een punt op de kaart dat gekoppeld is aan een plaats, wordt onderaan in het paneel de desbetreffende informatie erover getoond. Het geselecteerde punt kan gekoppeld zijn aan een plaats, straat of gebied (eventueel meerdere tegelijk) en daarom is het informatiepaneel onderaan verdeeld in 3 subpanelen. Elk subpaneel heeft een tabel die apart kan geselecteerd worden. Als het
Hoofdstuk 5. De kaartenapplicatie
35
Figuur 5.6: De kaartenapplicatie versie 1
geselecteerde punt gekoppeld is aan een plaats, straat of gebied, wordt dit in het desbetreffende paneel weergegeven. Dit wordt gerealiseerd met een JTabbedPane-object dat 3 panelen bevat met elk een JTable-object. De aanpak die in deze versie gebruikt is, heeft zoals eerder gezegd het voordeel dat er een scheiding is tussen de informatie die ingebracht is via de kaartenapplicatie en de informatie die afkomstig is van bij de conversie. Maar deze aanpak leidt anderzijds ook tot een sterk complexere GUI en bijgevolg ook complexere manier om data in geven. Daarom werd, zoals eerder aangegeven, tijdens de ontwikkeling van deze versie van de kaartapplicatie overgestapt op het aanvullend databankschema versie 4 (zie sectie 5.3.4). Hierdoor is niet verder gewerkt aan deze versie van de kaartenapplicatie en zijn enkel de GUI en enkele achterliggende methodes van de ‘backend’ ontwikkeld.
5.5
De kaartenapplicatie versie 2
Deze versie maakt gebruik van het aanvullende databankschema voor kaarten versie 4 (zie sectie 5.3.4) en daarom is het mogelijk om alle plaatsen op eenzelfde manier te behandelen. In
Hoofdstuk 5. De kaartenapplicatie
36
dit databankschema is het mogelijk om een gebied voor te stellen op een kaart door meerdere punten, maar in deze versie van de kaartenapplicatie is het enkel mogelijk om een gebied te bepalen door 1 punt. Daarom is het mogelijk om aan 1 punt op de kaart verschillende plaatsen te koppelen. Dit is handig om bijvoorbeeld de stad “Brugge” op een kaart aan te duiden aangezien deze plaats dan kan gekoppeld worden aan het punt op de kaart waar de markt of het stadhuis van “Brugge” gelegen is. Aan dit desbetreffende punt worden dus 2 plaatsen uit de databank gekoppeld.
5.5.1
Voorstelling programma
In de applicatie kan een kaart ingeladen worden in een paneel dat voorzien wordt van scrollbars indien de kaart te groot is voor het scherm. Als de kaart echter kleiner is dan dit paneel, wordt deze gecentreerd weergegeven in het paneel. Aan de linkerkant van dit kaartpaneel zijn een reeks knoppen geplaatst waarmee acties op plaatsen en kaarten kunnen verricht worden. Onderaan wordt in een tabel informatie over het geselecteerde punt op de kaart getoond.
Figuur 5.7: De kaartenapplicatie versie 2
Om plaatsen aan een kaart te kunnen toevoegen, moet de kaart eerst via deze applicatie aan de databank toegevoegd worden. Vervolgens kan de kaart worden ingeladen en worden
Hoofdstuk 5. De kaartenapplicatie
37
de eventuele punten op de kaart die gekoppeld zijn aan plaatsen uit de databank getoond. Deze punten worden aangegeven door een cirkel die gekleurd is volgens het type van de gekoppelde plaats. De legende die toont welke kleur er bij een bepaald type plaats hoort, staat in het linkerzijpaneel. Wanneer een punt op de kaart echter aan verschillende plaatsen uit de databank gekoppeld is, wordt de cirkel zwart gekleurd. Om een bestaand punt op de kaart te selecteren, klikt men er met de linkermuisknop op, waarna de cirkel rood kleurt en de plaatsen gekoppeld aan dit punt in de tabel onderaan getoond worden. Wanneer een plaats in deze tabel geselecteerd wordt, kan de plaats desgewenst gewijzigd, ontkoppeld of er kan een andere plaats aan het geselecteerde punt gekoppeld worden. Om een nieuw punt op de kaart te selecteren, klikt men met de rechtermuisknop op het gewenste punt op de kaart en verschijnt er op dat punt een rood kruis waaraan een plaats kan gekoppeld worden. Via het menu kan een helpbestand geraadpleegd worden dat geopend wordt in de standaard webbrowser. 5.5.1.1
Een kaart aan de databank toevoegen
Vooraleer een kaart kan gebruikt worden in de applicatie, moet dit kaartbestand in de map van de applicatie geplaatst worden en toegevoegd worden aan de databank. Dit toevoegen aan de databank gebeurt door op de knop “Kaart” in het “Voeg toe aan databank”-zijpaneel te klikken. Dan krijgt de gebruiker een scherm te zien waar de afbeelding kan geselecteerd worden, zoals getoond in figuur 5.8. Vervolgens wordt gevraagd naar een unieke naam voor de kaart en kan eventueel een periode voor de kaart ingegeven worden. Na deze acties wordt
Figuur 5.8: Dialoogscherm om de naam van een kaart in te geven
een bevestiging getoond die aangeeft dat de kaart aan de databank is toegevoegd en er dus plaatsen kunnen aan gekoppeld worden. 5.5.1.2
Een kaart in de applicatie inladen
Om een bestaande kaart in de applicatie te tonen, kan de gebruiker klikken op de knop “Andere kaart” in het “Kaartacties”-zijpaneel. Vervolgens krijgt de gebruiker een lijstje te zien met de in de databank aanwezige kaarten, waaruit 1 kaart kan geselecteerd worden. Dit is weergegeven in figuur 5.9. Na selectie wordt de desbetreffende kaart met de eventueel bijbehorende plaatsen ingeladen.
Hoofdstuk 5. De kaartenapplicatie
38
Figuur 5.9: Dialoogscherm kaartselectie
5.5.1.3
Een kaart sluiten of de eigenschappen ervan wijzigen
Om de kaart te sluiten die op dat moment getoond wordt in de applicatie, drukt de gebruiker op de knop “Sluiten” uit het “Kaartacties”-zijpaneel. Om de eigenschappen van de getoonde kaart te laten zien of wijzigen, kan op de knop “Eigenschappen” uit het “Kaartacties”-zijpaneel gedrukt worden, waarna de eigenschappen van de kaart getoond worden en kunnen gewijzigd worden, zoals te zien is in figuur 5.10.
Figuur 5.10: Dialoogscherm kaarteigenschappen
5.5.1.4
Een kaart uit de databank verwijderen
Om een bestaande kaart uit de databank te verwijderen, drukt de gebruiker op de knop “Kaart” uit het “Verwijder uit databank”-zijpaneel (het is niet nodig dat de desbetreffende kaart geopend is). Vervolgens wordt een dialoogscherm getoond waar de te verwijderen kaart kan geselecteerd worden uit een lijst met de in de databank aanwezige kaarten. Wanneer de kaart verwijderd wordt, worden ook alle punten op deze kaart die gekoppeld zijn aan een plaats uit de databank verwijderd. De gekoppelde plaatsen zelf worden echter niet verwijderd.
Hoofdstuk 5. De kaartenapplicatie 5.5.1.5
39
Een nieuwe plaats aan de databank toevoegen
Om een nieuwe plaats aan de databank toe te voegen, moet op de knop “Plaats” uit het “Voeg toe aan databank”-zijpaneel geklikt worden. Dan wordt volgend dialoogscherm uit figuur 5.11 getoond.
Figuur 5.11: Dialoogscherm om een plaats toe te voegen
Het is verplicht om een naam voor de plaats op te geven, de rest van de velden zijn facultatief in te vullen. De combinatie van de naam en het type moet uniek zijn in de databank. Om een ouderplaats toe te voegen aan de plaats, kan op de knop “Wijzigen” in het oudersubpaneel geklikt worden. Vervolgens wordt het dialoogscherm uit figuur 5.12 getoond, waar de velden om een ouderplaats te zoeken, kunnen ingevuld worden.
Figuur 5.12: Dialoogscherm om een ouderplaats toe te voegen
Men is niet verplicht om een veld in te vullen, indien men niets invult, worden alle plaatsen met dat bepaalde type getoond. Als zoekterm hoeft ook niet het gehele woord opgegeven te worden. Indien enkel een deel van het woord opgegeven wordt, zal de plaats ook gevonden
Hoofdstuk 5. De kaartenapplicatie
40
worden. Het type voor de ouderplaats kan logischerwijze enkel hoger zijn dan het type voor de plaats. Vervolgens krijgt de gebruiker een lijst te zien met de plaatsen die aan de zoekopdracht voldoen, waaruit een ouderplaats kan geselecteerd worden zoals ge¨ıllustreerd in figuur 5.13. Na het selecteren van de gewenste ouderplaats wordt terug het eerste dialoogscherm om een
Figuur 5.13: Dialoogscherm om een ouderplaats te selecteren
plaats toe te voegen, getoond zoals te zien in figuur 5.14.
Figuur 5.14: Dialoogscherm om een plaats te voegen
Op de figuur is te zien dat de ouderplaats is toegevoegd (eventueel kan die ook terug verwijderd worden).Door op de knop ”Voeg Toe”te klikken, wordt de plaats toegevoegd aan de databank.
Hoofdstuk 5. De kaartenapplicatie 5.5.1.6
41
Een plaats uit de databank wijzigen of verwijderen
Met de knop “Plaats” uit het “Wijzig”-zijpaneel kan een bestaande plaats uit de databank gewijzigd worden. Vervolgens kan een zoekterm opgegeven worden om de te wijzigen plaats te zoeken en wordt een procedure gevolgd analoog aan diegene om een ouderplaats te zoeken. Nu is er echter ook de mogelijkheid om aan te geven of het type ook geldt als zoekterm, door het al dan niet aanvinken van een checkbox die geplaatst is naast het type. Wanneer de plaats gevonden is, krijgt de gebruiker een dialoogscherm te zien, zoals weergegeven in figuur 5.15.
Figuur 5.15: Dialoogscherm om een plaats te wijzigen
Alle velden kunnen gewijzigd worden, zolang de combinatie van de naam en het type van een plaats in de databank maar uniek blijft. Men kan terug de ouderplaats verwijderen of wijzigen zoals eerder beschreven. Bij het veranderen van de ouderplaats moet het type van de ouderplaats echter wel hoger zijn dan het type van de plaats zelf. Indien het type van de plaats zelf veranderd wordt, dan moet het type logischerwijze ook lager zijn dan het type van de ouderplaats zelf. Het omgekeerde geldt ook, indien de plaats zelf ouder is van een andere kindplaats, dan moet het type groter zijn dan het type van deze kindplaats. Indien aan ´e´en van deze voorwaarden niet voldaan is, wordt een gepaste foutmelding weergegeven. Om een plaats te verwijderen, moet op de knop “Plaats” uit het “Verwijder uit databank”zijpaneel gedrukt worden en kan de plaats vervolgens gezocht worden via de reeds gekende zoek/selectie-procedure. Indien de ouderplaats van een plaats verwijderd wordt, worden bij alle kindplaatsen van deze plaats(plaatsen die dus deze plaats als ouder hebben) ook diezelfde ouder (en eventueel bovenliggende ouders) verwijderd. Indien een plaats verwijderd wordt uit de databank, dan wordt de plaats hierdoor ook losgekoppeld van alle punten op de kaart(en) waar de plaats eventueel aan gekoppeld was.
Hoofdstuk 5. De kaartenapplicatie 5.5.1.7
42
Een bestaande plaats uit de databank aan een nieuw punt op de kaart koppelen
Om een nieuw punt op de kaart te koppelen aan een plaats uit de databank, moet met de rechtermuisknop op de gewenste plaats op de kaart geklikt worden. Er wordt nu een rood kruis op deze plaats getekend en de knop “Plaats” uit het “Koppel aan databank”-paneel kan aangeklikt worden. Vervolgens kan de plaats gezocht en geselecteerd worden volgens de reeds besproken zoek/selectieprocedure. Wanneer uiteindelijk de juiste plaats uit de databank geselecteerd is en bevestigd, wordt er op het desbetreffende punt een cirkel getekend en ingekleurd volgens het type van de gekoppelde plaats. De plaats is nu gekoppeld aan het aangeduide punt op de kaart. 5.5.1.8
De plaatsen uit de databank gekoppeld aan een punt op de kaart aanpassen
Om de eigenschappen van een punt op de kaart dat reeds gekoppeld is aan een plaats te wijzigen, klikt men met de linkermuisknop op de cirkel horende bij dat punt. Hierdoor wordt de cirkel rood gekleurd en zijn in de tabel onderaan het scherm de plaats(en) die gekoppeld zijn aan dit punt te zien. Om een andere plaats aan dit punt te koppelen kan men op de knop “Koppel” onderaan het scherm drukken (men bekomt hetzelfde resultaat door op de knop “Koppel” bovenaan te drukken). Vervolgens is de procedure analoog als beschreven in bovenstaande paragraaf. Om de eigenschappen van een plaats gekoppeld aan een punt op de kaart te zien, selecteert de gebruiker eerst de plaats in de tabel onderaan en klikt hij vervolgens op knop ”Eigenschappen”die ernaast te vinden is. Verder is de procedure analoog aan die uit sectie 5.5.1.5. Na het selecteren van de plaats in de tabel onderaan, kan ook gekozen worden om de plaats te ontkoppelen. Hierdoor is die plaats niet langer verbonden met het punt op de kaart, de plaats wordt hierdoor echter niet verwijderd uit de databank. Om de plaats te ontkoppelen, wordt op de knop “Ontkoppel” onderaan het scherm gedrukt.
5.5.2
Programmastructuur
De Java-applicatie is ingedeeld in verschillende pakketten of zogenaamde Java-packages. Zoals op de figuur te zien is, zit het gehele programma vervat in het pakket kaartenapplicatie. Alles over de ‘Graphical User Interface’ van de applicatie staat in het pakket GUI, het pakket vo eronder bevat de ’View Objects’ voor de GUI. In het pakket accessdb staat de klasse met de connectie naar de databank toe en daar is ook het subpakket postgres geplaatst, de directe connectie met de PostgreSql-databank. Het pakket bo tenslotte bevat de ‘business objects’ voor de applicatie. In het pakket GUI worden alle klassen van de panelen voor schermopbouw, ActionListeners, dialoogschermen en enkele hulpklassen geplaatst. De klasse Content, een afgeleide van de
Hoofdstuk 5. De kaartenapplicatie
43
Figuur 5.16: De pakketstructuur
Java-klasse JPanel, is de belangrijkste klasse die hier te vinden is. De Main-klasse laadt een nieuw object van de Content-klasse in en dit Content-object bepaalt de volledige scherminhoud van het programma. De Content-klasse bevat als belangrijkste dataleden de objecten van de klassen ImageHolder, SimpleTable en enkele SidePanel -klassen. Het object van de ImageHolder -klasse zorgt voor het weergeven van de kaart. De klasse SimpleTable staat in voor de tabel onderaan het scherm die informatie toont over het geselecteerde punt op de kaart. De Sidepanel -klassen zorgen voor het zijpaneel met de knoppen aan de linkerkant van het scherm, zo heeft men de klassen SidePanelKoppel,SidePanelWijzig,SidePanelKaart... De SidePanelTypes-klasse geeft de legende met de types die gebruikt worden in het programma weer samen met de kleur van het type. Deze klassen worden in de volgende paragrafen besproken. 5.5.2.1
De ImageHolder -klasse
Het tekenen van de kaart en de cirkels De ImageHolder -klasse, afgeleid van de JPanel -klasse, moet zoals reeds eerder aangegeven een kaart kunnen weergeven en toelaten dat er objecten op deze kaart getekend worden. Probleem hierbij is dat men de grootte van de kaarten vooraf niet weet, hierdoor is het gebruik van scrollbars noodzakelijk. Wanneer de afbeelding kleiner is dan het tekengebied, dan wordt de kaart best gecentreerd weergegeven. Voorlopig worden de afbeelding en de te erop te tekenen objecten voor de duidelijkheid nog getekend op een gewoon JPanel -object zonder scrollbars. De werkwijze is hetzelfde aangezien de JPanel -klasse de ouderklasse is van de ImageHolder -klasse. De afbeelding zal bijgehouden worden in het kaartlabel -object,dat een object is van de Java-klasse JLabel. Om de afbeelding weer te geven wordt gebruik gemaakt
Hoofdstuk 5. De kaartenapplicatie
44
van de ImageIcon-klasse. Aan deze klasse wordt het pad van de kaartafbeelding meegegeven worden met de constructor van het ImageIcon-object. Het ImageIcon-object kan vervolgens meegeven worden met de constructor van de JLabel -klasse waarin de afbeelding weergegeven wordt of meegegeven via de methode setIcon. De werkwijze wordt ge¨ıllustreerd in volgende code van de desbetreffende JPanel-klasse. De afbeelding “brugge.jpg” zou afgebeeld worden op het scherm indien het JPanel -object zou toegevoegd worden aan het Content-object. ImageIcon imageicon=new ImageIcon("brugge.jpg"); JLabel kaartlabel=new JLabel(imageicon); add(kaartlabel); Om de punten weer te geven die gekoppeld zijn aan plaatsen op een kaart, wordt er geopteerd om dit voor te stellen door middel van cirkels. In JAVA zijn er voor het tekenen verschillende methodes beschikbaar. Zo is er de methode paint(Graphics g) die ontwikkeld is voor de AWTcomponenten in de JDK 1.0-versie. Met de JDK 1.1-versie is de Swing-toolkit ontwikkeld die vergelijkbaar is met die van AWT en er zelf ook gebruik van maakt. Maar er zijn ook enkele verschillen die het tekenen met Swing makkelijker maken, daarom wordt er hier gekozen voor de Swing-tekenmethoden (Articles Java, 2007). Swing voorziet hiervoor de methode paintComponent(Graphics g), deze methode roept op zijn beurt de voor zichzelf sprekende methodes op in volgorde: protected void paintComponent(Graphics g) protected void paintBorder(Graphics g) protected void paintChildren(Graphics g) Het tekenen in een JPanel -object of paneel kan door de methode paintComponent(Graphics g) van deze klasse te overschrijven zoals in onderstaande code. De aanroep naar super zorgt ervoor dat de methode uitgevoerd wordt via de bovenliggende klasse. (paintComponent(Graphics g) is namelijk een methode van de topklasse JComponent). Via volgende code tekent men een rode cirkel op het paneel: public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.red); g.fillOval(100,100,30,30); } Om nu een kaart in dit paneel te tekenen en er vervolgens een rode cirkel op te tekenen, zou volgende code kunnen uitgevoerd worden de JPanel -klasse: ImageIcon imageicon=new ImageIcon("brugge.jpg"); JLabel kaartlabel=new JLabel(imageicon);
Hoofdstuk 5. De kaartenapplicatie
45
add(kaartlabel); //overschrijven van de methode paintcomponent public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.red); g.fillOval(100,100,30,30); } Het probleem is echter dat de cirkel die op de afbeelding moet worden getekend, erachter wordt getekend. Daarom is in eerste instantie gekeken voor een andere methode om een afbeelding op het scherm weer te geven, die geen gebruik van de klasse ImageIcon. Maar voor deze methode besproken wordt, gaan we eerst bespreken hoe aan de kaartafbeelding scrollbars kunnen worden toegevoegd. Hiervoor maken we gebruik van de eerder aangehaalde ImageHolder -klasse. We maken ook gebruik van een nieuwe klasse DrawLabel, die de variabele kaartafbeelding uit de vorige stukken code gaat vervangen. Deze DrawLabel -klasse is afgeleid van de JLabel -klasse en bevat de kaartafbeelding. Ook het tekenen op de afbeelding gebeurt in deze klasse. Het DrawLabel -object wordt aangemaakt bij aanmaak van het ImageHolder -object, die het DrawLabel -object vervolgens voorziet van scrollbars door het object in een JScrollPane-object te stoppen. Dit wordt ge¨ıllustreerd in volgende code van de ImageHolder -klasse waarvan het object aan het Content-object moet toegevoegd worden: /** Creates a new instance of ImageHolder */ //het eerder genoemde content-object wordt meegegeven //om de methodes ervan te kunnen aanspreken public ImageHolder(Content content) { this.content=content; setLayout(new BorderLayout()); drawlabel=new DrawLabel(); scroll=new JScrollPane(drawlabel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); add(scroll,BorderLayout.CENTER); } Hierdoor worden er enkel scrollbars toegevoegd aan het DrawLabel -object indien de kaart die in dit object weergegeven wordt, groter is dan het ImageHolder -object. De afbeelding wordt nu ingeladen via de Image-klasse, dit is een abstracte klasse van de klassen die grafische afbeeldingen weergeven. Het inlezen van de afbeelding gebeurt via de
Hoofdstuk 5. De kaartenapplicatie
46
abstracte klasse Toolkit. Wanneer men vervolgens een Image-object op het scherm wil weergeven, gebeurt dit door een aanroep naar de methode drawImage(...). Image image = Toolkit.getDefaultToolkit().getImage("D:/brugge.jpg"); //overschrijven van de methode paintcomponent public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image,0,0,this); g.setColor(Color.red); g.fillOval(100,100,30,30); } De bovenstaande code moet uitgevoerd worden in de Drawlabel -klasse, die zoals eerder vermeld, via de ImageHolder -klasse in een JScrollPane-object gestopt wordt. Nu wordt de cirkel correct bovenop de afbeelding getekend, maar hebben we te maken met een nieuw probleem. Indien deze methode uitgevoerd wordt, blijkt dat er geen scrollbars tevoorschijn komen indien de afbeelding groter is dan het DrawLabel -paneel. De afbeelding wordt in dat geval gewoon op ware grootte getekend in dit paneel en de stukken die niet binnen het paneel vallen worden weggelaten. Een verklaring voor dit resultaat kan gevonden worden door de methode drawImage(...) eens van naderbij te bekijken. De methodeaanroep met parameters ziet er als volgt uit: drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) Het ImageObserver-object dat meegegeven moet worden als parameter is het object dat wordt gewaarschuwd als de afbeelding klaar is. Hiervoor kan men het DrawLabel -object zelf nemen of gewoon null. De getallen dx1 en dy1 stellen de linkerbovenhoek voor waar moet gestart worden met tekenen en samen met dx2 en dy2 wordt dus een rechthoek bepaald waarin de methode drawImage(...) het image-object op het scherm tekent. Het stuk van de afbeelding dat effectief getekend wordt, is bepaald door de s-waarden die een analoge rechthoek voorstellen. De verhoudingen tussen de rechthoek bepaald door de d -waarden en de rechthoek bepaald door de s-waarden bepalen dus de schaling van de afbeelding. De rechthoek van de afbeelding (bepaald door de s-waarden) wordt ingepast in de rechthoek van het tekengebied (bepaald door de d -waarden). Wanneer de rechthoek bepaald door de d -waarden kleiner is dan de rechthoek bepaald door de s-waarden, wordt de afbeelding verkleind zodat hij in de eerste rechthoek past. Als de opgegeven rechthoek van het tekengebied (bepaald door de d -waarden) groter is dan het tekengebied dat beschikbaar is in het object(bij ons een DrawLabel -object), wordt enkel het gedeelte getekend dat ook effectief kan weergegeven worden in het object. Met andere woorden is het gebied waarin getekend wordt vast en bepaald door de rechthoek
Hoofdstuk 5. De kaartenapplicatie
47
met d-waarden of kleiner indien deze rechthoek groter is dan het in het object beschikbare tekengebied. Aangezien het gebied waarin getekend wordt dus nooit groter zal zijn dan de maximale grootte van het DrawLabel -object zullen er nooit scrollbars getoond worden via het JScrollPane-object, waarin het DrawLabel -object vervat zit, zelfs al is de afbeelding groter en wordt ze niet volledig getekend. Ook wanneer gebruik gemaakt wordt van de methode paintIcon(...) van de Image-klasse heeft men hetzelfde resultaat: Image img=new ImageIcon("D:/brugge.jpg"); img.paintIcon(this,graphics,0,0); Bijgevolg zijn deze methodes op deze manier niet bruikbaar voor het weergeven van onze kaarten in een JScrollPane-object. Er moet dus gezocht worden naar een oplossing die geen gebruik maakt van de methodes drawImage(...) of paintIcon(...). Een oplossing die dit verwezenlijkt, overschrijft de tekenmethodes van een van de klassen die de kaart en de cirkels op het scherm weergeven op een zodanige manier dat de volgorde waarin de objecten getekend worden, verandert. De volgorde waarin objecten getekend worden, bepaalt namelijk welk object vanboven komt te staan en dus zichtbaar is. Een eerste oplossing breidt de Java-klasse JViewPort uit. Een JViewPort-object is het venster waardoor het zichtbare gedeelte van het JScrollPane-object te zien is en is dus een soort van view. Bij deze manier van werken wordt het DrawLabel -object nog steeds in het JScrollPane-object gestopt, maar nu koppelt men dit JScrollPane-object aan een object van de uitgebreide JViewPort-klasse. In deze uitgebreide JViewPort-klasse wordt de paintComponent(Graphics g) zo overschreven zodat eerst de gewone view (met enkel de kaart) wordt getekend en vervolgens de cirkels erop getekend worden. Deze manier van werken is echter niet bruikbaar aangezien de cirkels een vaste plaats ten opzichte van de kaart zouden hebben en bij deze werkwijze zouden de cirkels niet mee bewegen als we een ander stuk van de kaart te zien krijgen door te scrollen. De cirkels zijn als het ware vastgeplakt op de view. Deze methode kan wel gebruikt worden als ze op een andere klasse wordt toegepast en dit wordt in de volgende paragraaf verder ge¨ıllustreerd. Een volgende oplossing breidt de Java ImageIcon-klasse uit. Hierbij wordt de afbeelding op de gebruikelijke manier in het DrawIcon ingesteld via de super-aanroep. In de overschreven methode paintComponent(Graphics g) wordt nu echter eerst een aanroep naar de bovenliggende klasse ImageIcon gedaan waarin de afbeelding getekend wordt. Vervolgens wordt de cirkel hierop getekend en is deze dus zichtbaar. Dit wordt ge¨ıllustreerd in volgende code:
Hoofdstuk 5. De kaartenapplicatie
48
public class DrawIcon extends ImageIcon { public DrawIcon(String pad){ super(pad); } //overschrijven van de methode paintcomponent public void paintComponent(Graphics g){ super.paintComponent(g); // geeft compilatiefout g.setColor(Color.red); g.fillOval(100,100,30,30); } } De opdracht super.paintComponent(g) die dezelfde methode van de ouderklasse ImageIcon aanroept geeft echter een compilatiefout. De reden hiervoor is dat de ImageIcon-klasse rechtstreeks is afgeleid van de Object-klasse en dus niet van de basisklasse voor Swingcomponenten JComponent. De Object-klasse bevat geen methode paintComponent(Graphics g) zoals de JComponent-klasse en hierdoor kan ook geen gebruik gemaakt worden van deze methode in de uitgebreide klasse DrawIcon. De uiteindelijke oplossing bestaat erin om de paintComponent(Graphics g) van deDrawlLabel klasse te overschrijven waarvan het object in het JScrollPane-object geplaatst is en het ImageIcon-object bevat. De code voor de DrawLabel -klasse ziet er dan als volgt uit: public class DrawLabel extends JLabel { /** Creates a new instance of DrawLabel */ public DrawLabel(ImageIcon icon) { super(icon); } public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(Color.red); g.fillOval(100,100,30,30); } } Hierbij wordt het ImageIcon-object doorgegeven aan de constructor van de bovenliggende JPanel -klasse. Het ImageIcon-object wordt ge¨ınitialiseerd in het ImageHolder -object en doorgegeven naar het DrawLabel -object. In de methode paintComponent(Graphics g) wordt nu eerst via de aanroep naar de bovenliggende klasse het ImageIcon-object getekend en vervolgens (en dus erboven) een cirkel. Dit DrawLabel -object wordt dan via de reeds getoonde code in een JScrollPane-object gestopt in het ImageHolder -object. Met deze oplossing is het dus
Hoofdstuk 5. De kaartenapplicatie
49
mogelijk om te scrollen indien de afbeelding te groot is en wordt de cirkel correct bovenop de afbeelding getekend. Het opvangen van muisklikken Nu zijn we dus in staat om de kaart en de bijbehorende cirkels, die gekoppelde plaatsen voorstellen, weer te geven op een kaart. De gebruiker moet een bestaand punt op de kaart kunnen selecteren om de eigenschappen ervan te kunnen wijzigen en moet ook in staat zijn om een nieuw punt op de kaart te selecteren. Bij het selecteren van een bestaand punt moet de cirkel rood worden en bij het aanklikken van een nieuw punt moet een rood kruis getekend worden. In eerste instantie werd gedacht om een bestaand punt te selecteren door een enkele muisklik en een nieuw punt te laten toevoegen door een dubbele muisklik. Om enkele en dubbele klikken te kunnen onderscheiden kan volgende code in de ImageHolder -klasse geplaatst worden: addMouseListener(new MouseAdapter() { public void mouseClicked (MouseEvent e) { if(e.getClickCount() == 1) System.out.println("er werd ´ e´ en maal geklikt"); else if (e.getClickCount() == 2) System.out.println("er werd tweemaal geklikt"); } }); Deze methode doet echter niet wat men direct zou verwachten. Wanneer men dubbelklikt wordt er eerst een MouseEvent gegenereerd waarbij de ClickCount gelijk is aan ´e´en, vervolgens wordt nog een MouseEvent gegenereerd waarbij de ClickCount gelijk is aan twee (Bugs Database Java, 2007a). Het opvangen van een dubbele muisklik wordt dus wel erg moeilijk als men hierbij geen code bestemd voor de enkele muisklik wil uitvoeren. Er is echter een oplossing mogelijk door gebruik te maken van de Timer -klasse waarbij de tijd tussen 2 muisklikken bijgehouden wordt. Aangezien deze oplossing nogal ‘dirty’ kan genoemd worden, is er gekozen om een bestaand punt te selecteren met de linkermuisknop en een nieuw punt met de rechtermuisknop. De code ziet er dan als volgt uit: addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.getButton()== MouseEvent.BUTTON1) drawLabelLeftClick(e.getX(),e.getY()); else if (e.getButton()== MouseEvent.BUTTON3) drawLabelRightClick(e.getX(),e.getY());
Hoofdstuk 5. De kaartenapplicatie
50
} }); Coo ¨rdinatentransformatie Wanneer de afbeelding groter is dan het gebied voorzien voor het tekenen van de kaart, de zogenaamde viewport, worden er zoals eerder gezegd scrollbars getekend. Wanneer de afbeelding echter kleiner is dan de viewport, moet de afbeelding gecentreerd worden in dit gebied. Dit wordt automatisch gedaan indien men een ImageIcon-object toevoegt aan een JLabel -object op de reeds geziene wijze. Dit gebeurt dus ook automatisch in onze applicatie aangezien de DrawLabel -klasse afgeleid is van de JLabel -klasse. Er moet echter ook voorzien worden dat de gebruiker bij een kaart kleiner dan de viewport niet kan klikken op het gebied naast de kaart. Indien er op de afbeelding geklikt wordt, moet er een co¨ordinatentransformatie doorgevoerd worden die de muisco¨ ordinaat omzet in de co¨ordinaat op de afbeelding. Deze transformatie is nodig aangezien de muisco¨ ordinaat relatief is ten opzichte van de linkerbovenhoek van het DrawLabel -object en wij hebben de co¨ordinaat ten opzichte van de linkerbovenhoek van de afbeelding nodig. De situatie is ge¨ıllustreerd in volgende afbeelding. Wanneer men een nieu-
Figuur 5.17: De situatie bij een afbeelding kleiner dan de viewport
we kaartafbeelding in de applicatie inlaadt, wordt er eerst gecontroleerd of de afbeelding niet kleiner is dan de viewport. Indien dit het geval is worden de variabelen RandX en RandY berekend door het vergelijken van de groottes van het ImageIcon-object en het JViewport-object. Het controleren en instellen van de variabelen gebeuren beide in het ImageHolder -object. Wanneer er nu een muisklik gedetecteerd wordt op het ImageHolder -paneel, wordt er eerst in hetzelfde object gekeken via deze variabelen of de muisklik wel degelijk op de afbeelding was. Indien dit het geval is wordt de muisco¨ordinaat omgezet naar de co¨ordinaat op de kaart en wordt dit via een methodeaanroep in het Content-object aan de rest van het programma doorgegeven. Dit betekent concreet dat enkel het ImageHolder - en het DrawLabel -object op de hoogte zijn van de absolute muisco¨ordinaten op het ImageHolder -paneel. In de rest van
Hoofdstuk 5. De kaartenapplicatie
51
het programma wordt enkel gewerkt met de absolute co¨ordinaten van de punten ten opzichte van de kaart. De VPlaatsCoordinaat-klasse Wanneer men een nieuwe kaart inlaadt in de applicatie, moeten ook alle punten op de kaarten die verbonden zijn met een plaats aangeduid worden op deze kaart door middel van een cirkel. Hiervoor worden objecten van de VPlaatsCoordinaat-klasse uit het vo- of View Objects-pakket gebruikt. De dataleden van deze klasse zijn: int x,y; private String naam,type,nr,actuelenaam,oudernaam,oudertype; Een object van de deze klasse houdt dus de informatie over een plaats bij en de positie op de huidig getoonde kaartafbeelding van deze plaats. De getallen x en y stellen de positie op de kaartafbeelding voor, de velden naam, type, nr, actuelenaam zijn informatievelden van de plaats zelf. De velden oudernaam en oudertype zijn echter informatievelden over een andere plaats, namelijk de ouderplaats. Dit hoort dus in strikte zin niet echt thuis bij het object. Alle klassen die men echter vindt in het pakket View Objects en dus ook de VPlaatsCoordinaatklasse worden opgeslagen via de informatie die erover getoond wordt in een deel applicatie. Indien men een punt op de kaart selecteert, wordt alle informatie over deze plaats in de tabel onderaan getoond en dus ook de informatie over de ouderplaats. Om vast te stellen op welke cirkel van een VPlaatsCoordinaat-object er juist geklikt is wordt de afstand vergeleken tussen de co¨ ordinaat van dit object(bepaald door x en y) en de co¨ordinaat van de muisklik. Dit werd eerst verwezenlijkt door zelfgeschreven code, maar er is een beter alternatief. Er kan gebruik gemaakt worden van de distance(Point2D pt)-methode, te vinden in de statische klasse Point2D. Met deze methode berekent men de afstand tussen 2 punten. Het VPlaatsCoordinaat-object met de kleinst gevonden afstand wordt dan via het rood worden van de cirkel geselecteerd. 5.5.2.2
de SimpleTable-klasse
De klasse SimpleTable is afgeleid van de JPanel -klasse en bevat een JTable-object. Wanneer een punt op de kaart geselecteerd wordt, ziet men in deze tabel meer informatie over dat punt. In figuur 5.18 is het punt op de kaart geselecteerd dat gekoppeld is aan de plaats “Zandvoordewijk”. Het is zoals eerder vermeld mogelijk dat er aan een punt op de kaart meerdere plaatsen verbonden zijn. Indien dit het geval is, staan alle plaatsen verbonden aan het geselecteerde punt in deze tabel. Dit is erg makkelijk aangezien alle velden die in deze tabel staan(“Naam”, “Type”, “Ouderplaats”, “Nummer” en “Actuele Naam”) te vinden zijn in de velden van het desbetreffende VPlaatsCoordinaat-object. Het opvullen van een dergelijk
Hoofdstuk 5. De kaartenapplicatie
52
Figuur 5.18: Het tabelpaneel
JTable-object gebeurt via een object van een klasse die afgeleid is van de AbstractTableModel klasse. Een object van deze afgeleide klasse kan meegegeven worden met de constructor van het JTable-object. De belangrijkste code voor de afgeleide klasse van de AbstractTableModel klasse, TableModel genaamd, ziet er als volgt uit: public class TableModel extends AbstractTableModel { Object[][] data; String[] columnNames; // Creates a new instance of TableModel public TableModel(Object[][] data,String[] columnNames) { this.data=data; this.columnNames=columnNames; } public Object getValueAt(int row, int col) {...} public int getColumnCount() {...} public int getRowCount() {...} public String getColumnName(int col) { return columnNames[col]; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } void setData(String[][] datatable) { data=datatable; } void setColumns(String[] columnNames) {...} De inhoud van het JTable-object wordt dus bijgehouden in het daaraan gekoppelde TableModel -object. Wanneer men de data wil veranderen, gebeurt dit door de methode setData(String[][] datatable). Vervolgens moet de methode fireTableDataChanged() van de AbstractTableModel -klasse aangeroepen worden (deze methode kan dus ook in onze afgeleide klasse opgeroepen worden). Deze methode genereert een Event en zorgt ervoor de het JTable-object hertekend wordt.
Hoofdstuk 5. De kaartenapplicatie
53
Er is echter een probleem wanneer een van de velden van het VPlaatsCoordinaat-object niet ingesteld is en dus gelijk aan null. Dit kan dikwijls voorkomen met bijvoorbeeld het dataveld nr of actuelenaam. Het probleem is dat Java bij het tekenen van de tabel gebruik maakt van de methode getColumnClass van de TableModel -klasse. Deze methode gebruikt de eerste rij in de tabel en vraagt de klasse op van iedere individuele cel door middel van de opdracht getValueAt(0, c).getClass() zoals te zien is in de code. Wanneer het VPlaatsCoordinaat-object dat op deze rij staat echter bij ´e´en van de cellen gelijk is aan null, wordt een NullPointerException opgeworpen bij gebruik van deze methode. Een oplossing voor dit probleem zorgt ervoor dat geen enkel dataveld in de VPlaatsCoordinaat-klasse gelijk is aan null. Indien de waarde voor dit dataveld onbepaald is, wordt de waarde gelijk gesteld aan “” of de ledige string. Dit is perfect mogelijk aangezien alle datavelden van het type String zijn en objecten van deze klasse enkel gebruikt worden voor presentatie op het scherm. Er zijn zeker ook nog andere oplossingen voor dit probleem, maar we zullen later zien dat deze manier ook nog andere voordelen heeft. Deze manier wordt zelfs uitgebreid naar iedere klasse die te vinden is in het View Objects(vo)-pakket. Geen enkele van de datavelden van ´e´en van deze klassen mag null bevatten als waarde. 5.5.2.3
de SidePanel -klassen
Deze klassen zorgen voor het knoppenzijpaneel aan de linkerkant van het scherm en zijn een afgeleide klasse van de JPanel -klasse. De SidePanel -klassen zijn de voor zichzelf sprekende klassen SidePanelKaart, SidePanelKoppel, SidePanelVerwijder, SidePanelVoegToe en SidePanelWijzig. De klasse SidePanelTypes zorgt voor een legende voorgesteld door een tabel met de aanwezige types van plaatsen en de daarbij behorende kleur. In figuur 5.7 is dit zijpaneel duidelijk te zien. De grootte van de knoppen is afhankelijk van de schermresolutie zodat iedere knop zeker goed zichtbaar is en er niet teveel lege ruimte is in het paneel. De applicatie wordt bij het opstarten automatisch gemaximaliseerd en de venstergrootte kan ook niet veranderd worden. Indien het venster namelijk verkleind zou kunnen worden, is het mogelijk dat de knoppen te klein zouden worden om nog te kunnen weergeven en de mogelijkheid tot veranderen van de venstergrootte wordt bij deze kaartenapplicatie ook niet gezien als een meerwaarde voor het programma. Het maximaliseren en vastzetten van de venstergrootte wordt gerealiseerd met volgende code: setExtendedState(...) //moet voor setVisible staan venster.setExtendedState(JFrame.MAXIMIZED_BOTH); venster.setVisible(true); venster.setResizable(false); Wanneer er echter dubbel geklikt wordt op de titelbalk van het programma in het besturingssysteem MS Windows, wordt het programmavenster toch verkleind. Daar kan echter niet
Hoofdstuk 5. De kaartenapplicatie
54
direct iets aan veranderd worden aangezien het een gekende bug is in Java in combinatie met MS Windows.(Bugs Database Java, 2007b) 5.5.2.4
De Content-klasse
Alle objecten van de besproken klassen worden ge¨ınitialiseerd in het object van de Content-klasse. Dit object is het inhoudspaneel voor de gehele applicatie. Wanneer men op een knop van het zijpaneel klikt, wordt via het aan de knop gekoppelde ActionListener -object, de desbetreffende methode van de Content-klasse uitgevoerd. Meestal is dit het openen van een dialoogvenster, alle begindialoogschermen worden namelijk vanuit het Content-object geopend. Veel van deze dialoogschermen hebben een vergelijkbare inhoud, zo moet er bij het zoeken, wijzigen of verwijderen van een plaats altijd eerst gezocht worden naar de plaats. Daarom is er een basisdialoogscherm DialogSelecteerPlaats ontwikkeld voor het selecteren van een plaats uit een lijst van plaatsen. Deze klasse wordt gebruikt als basisklasse voor de klassen DialogSelecteerKoppelPlaats, DialogSelecteerVerwijderPlaats, DialogSelecteerWijzigPlaats en DialogSelecteerOuderPlaats. Een object van de DialogSelecteerWijzigPlaats-klasse kan men zien in figuur 5.19. In de uitgebreide klassen wordt dan het specifieke daaropvolgende dia-
Figuur 5.19: Een object van de DialogSelecteerWijzigPlaats-klasse
loogscherm geselecteerd (DialogWijzigPlaats in het geval van DialogSelecteerWijzigPlaats). Voor het specifieke zoeken naar een plaats is de basisklasse DialogZoekPlaats voorzien, deze klasse wordt uitgebreid door vergelijkbare klassen als bij het uitbreiden van de DialogSelecteerPlaats-klasse. Verder is er voor de klassen DialogWijzigPlaats en DialogVoegToePlaats de basisklasse DialogVoegToeWijzigPlaats voorzien. De informatie over plaatsen die in figuur 5.19 zijn weergegeven, wordt logischerwijze ook gehaald uit objecten van een klasse uit het View Objects-pakket. Dit gebeurt nu echter niet
Hoofdstuk 5. De kaartenapplicatie
55
door de PlaatsCoordinaat-klasse aangezien de plaatsen die gezocht worden niet noodzakelijk moeten verbonden zijn met de huidig getoonde kaart (tijdens het zoeken hoeft er zelfs geen kaart getoond te worden). Hier wordt er gebruik gemaakt van de VPlaats-klasse. Deze klasse is vergelijkbaar met de PlaatsCoordinaat-klasse, enkel zijn de dataleden x en y eruit verwijderd aangezien er nu geen co¨ ordinaat op een kaart meer hoeft gespecificeerd te worden. Er wordt in de verschillende dialoogschermen altijd gebruik gemaakt van de Vplaats-klasse voor het weergeven van de plaatsen. Uiteraard geldt voor de VPlaats-klasse ook dat er geen datavelden mogelijk gelijk zijn aan null. Er is nog ´e´en klasse in het View Objects-pakket die we niet besproken hebben en dat is de klasse VKaart. Deze klasse bevat twee datavelden, ´e´en voor de naam van de kaart en ´e´en voor de periode en wordt gebruikt in het dialoogscherm om de kaarteigenschappen weer te geven. Het verbieden dat een dataveld van de klassen van het View Objects-pakket gelijk mag zijn aan null heeft zoals eerder gezegd nog een bijkomend voordeel. Wanneer men namelijk tekst opvraagt uit een tekstveld en dit tekstveld is niet ingevuld, dan geeft de getText()-functie van het tekstveld ook de ledige string terug en niet null. Wanneer we bijvoorbeeld een plaats willen wijzigen(zie fig 5.15) dan moet er in dit dialoogvenster eerst gecontroleerd worden of ´e´en van de velden veranderd is. Pas dan mag het mogelijk zijn om op de knop “Wijzigen” te drukken. Dit controleren kan men doen door de datavelden van het oorspronkelijke VPlaats-object te vergelijken met de eventueel veranderde waarden in de tekstvelden. Dit wordt onder andere verwezenlijkt door volgende code: if(naam.getText().compareTo(vplaats.getNaam())!=0 || actuelenaam.getText().compareTo(vplaats.getActueleNaam())!=0 || nr.getText().compareTo(vplaats.getNr())!=0 ...) buttonok.setEnabled(true); Zoals uit de code blijkt kan direct getest worden of de strings gelijk zijn via de methode compareTo(String str) en hoeft er niet eerst getest te worden of vplaats.getNr() gelijk is aan null waardoor de methode een NullPointerException zou opwerpen. Het enige nadeel aan deze manier van werken is dat er bij het doorvoeren van wijzigingen naar de databank toe, ervoor moet gezorgd worden dat de ledige string niet ingevuld wordt in het corresponderende dataveld van het record in de databank. Wanneer er data moet opgehaald of geschreven worden naar de databank gebeurt dit altijd via een methode of functie in de Content-klasse. In dit Content-object wordt namelijk bij initialisatie een object van de AccessDb-klasse aangemaakt die toegang naar de databank voorziet en een onderdeel is van het accessdb-pakket. Deze klasse maakt op zijn beurt bij initialisatie een object van de PostgresAccess-klasse aan die toegang voorziet naar de specifieke PostgreSqldatabank, deze klasse is onderdeel van het accesdb.postgres-pakket. De AccessDb-klasse fungeert dus als tussenlaag tussen het GUI -pakket en het accessbd.postgres-pakket (zie figuur
Hoofdstuk 5. De kaartenapplicatie
56
5.16). Het opzetten van een dergelijke constructie vergemakkelijkt het eventuele veranderen naar een andere databank aangezien er in het GUI -pakket niets hoeft veranderd te worden. Bij initialisatie van het AccessDb-object worden ook eenmalig de verschillende types voor de plaatsen uit de databank opgehaald. In de Postgres-klasse wordt de PostgreSql-databank direct aangesproken en via Prepared Statement-objecten en ResultSet-objecten wordt de informatie uit de databank gehaald. De benodigde “SELECT”-statements voor het aanspreken van de databank worden gelezen uit een Properties-bestand. Het doorgeven van data tussen het GUI - en accessdb-pakket gebeurt door de interfaces die te vinden zijn in het bo- of Business Objects-pakket. In dit pakket vindt men de klassen Plaats, PlaatsCoordinaat en Kaart en hun overeenkomstige interfaces. Deze klassen zijn vergelijkbaar met de overeenkomstige klassen uit het View Object- of vo-pakket, enkel geldt hier de restrictie niet dat datavelden verschillend moeten zijn van null. Wanneer er bij een plaats bijvoorbeeld geen nummer ingesteld is, dan heeft het dataveld nr uit hetPlaats-object van het bo-pakket als waarde null en bij het overeenkomstige VPlaats-object is de waarde van nr dan gelijk aan de ledige string.
5.5.3 5.5.3.1
Randopmerkingen/problemen Het toevoegen van een propertiesbestand aan het project
Zoals reeds eerder vermeld worden de SELECT -statements voor het raadplegen van de databank opgeslagen in een Properties-bestand. Dit bestand werd in eerste instantie toegevoegd aan de zelf gemaakte map “db” in de NetBeans-projectmap. Vervolgens werd deze “db”-map toegevoegd aan de “Libraries” van het project in NetBeans (aan “Libraries kan men bronnen toevoegen die nodig zijn voor het project) en werd het bestand via volgende code benaderd: ResourceBundle bdl=ResourceBundle.getBundle("postgres"); String driver=bdl.getString("DRIVER"); Wanneer de applicatie vervolgens uitgevoerd werd vanuit NetBeans, was er geen enkel probleem. Maar als men het JAR-bestand van het gehele project uitvoerde, bleek het mis te lopen. In het JAR-bestand bleek de Properties-file niet aanwezig te zijn (de inhoud van een JAR-bestand kan men zien door de opdracht jar tf KaartenApplicatie.jar uit te voeren). Vervolgens is het bestand geplaatst in de map bij de andere Java-bronbestanden in het accessdb.postgres-pakket. Wanneer het programma dan via NetBeans uitgevoerd werd, bleek het Properties-bestand niet gevonden te worden. Er werd een fout opgeworpen met als vermelding “Can’t find bundle for base name postgres, locale en GB” (Het uitvoerende besturingssysteem heeft een Britse taalinstelling en het Properties-bestand heet “postgres.properties”). Properties-bestanden worden echter veelal gebruikt om het programma in verschillende talen te kunnen laten functioneren. Daarom wordt in dit geval gezocht naar een Properties-bestand
Hoofdstuk 5. De kaartenapplicatie
57
voor Engels, maar deze taalinstelling is niet gespecificeerd voor ons bestand. Een oplossing voor dit probleem werd gevonden door het Properties-bestand op een alternatieve manier te benaderen volgens de hierop volgende code die het bestand wel correct inleest. InputStream inputStream = this.getClass() .getResourceAsStream("postgres.properties"); Properties properties = new Properties(); properties.load(inputStream); String driver=properties.getProperty("DRIVER")); 5.5.3.2
Meerdere lijnen in een JLabel
De tekst die getoond wordt in een JLabel -object stelt men in via de setText(String str)methode van deze klasse. Wanneer men echter deze tekst wil spreiden over verschillende lijnen in het JLabel -object zou men logischerwijze volgende code kunnen gebruiken: label.setText("Let op!! \n \n Hiermee verwijdert u de kaart uit de databank \n en alle bijbehorende co¨ ordinaten van plaatsen"); Deze code doet echter niet wat men direct zou verwachten. Het Java-newline-teken wordt gewoon mee in het label afgedrukt en er wordt dus geen nieuwe lijn genomen. De oplossing bestaat erin HTML-code in deze methode te gebruiken. label.setText(" Let op!!
Hiermee verwijdert u de kaart uit de databank
en alle bijbehorende co¨ ordinaten van plaatsen"); Nu wordt de tekst wel zoals gewenst in verschillende lijnen op het label -object getoond. 5.5.3.3
Controleren of een bestand een afbeelding is
Wanneer een nieuwe kaartafbeelding aan de databank toegevoegd wordt, moet er steeds gecontroleerd worden of de afbeelding kan worden geopend. Een bestand die een afbeeldingsextensie bezit, hoeft immers per definitie geen afbeelding te zijn. Dit wordt gerealiseerd na het selecteren van de nieuwe bestand door volgende code: ImageIcon icon=new ImageIcon(file.getName()); if(icon.getImageLoadStatus()==MediaTracker.ERRORED) JOptionPane.showMessageDialog(this,"...", "Fout",JOptionPane.ERROR_MESSAGE);
Deel III
De Webapplicatie
58
HOOFDSTUK
6
Technologieverkenning voor de webapplicatie
In dit hoofdstuk worden enkele technologie¨en besproken die kunnen gebruikt worden bij de ontwikkeling van de webapplicatie.
6.1
Ajax
Voor de weergave van de data uit de databank in de webapplicatie zijn relatief grote datatransfers nodig. Het is belangrijk dat deze data vlug opgehaald kan worden en dat het inladen van de data de gebruiker ook niet te veel stoort. Een technologiebenadering die hiervoor in aanmerking komt is Ajax of ‘Asynchronous JavaScript and XML’. Deze innoverende waaier van technologie¨en stapt af van het “click-and-wait”-principe zoals bij klassieke webpagina’s. De term werd ge¨ıntroduceerd in februari 2005 door Jesse James Garrett in een artikel op de website van “Adaptive Path” waar onder meer het volgende te lezen is: (Garrett, 2005) Google Suggest and Google Maps are two examples of a new approach to web applications that we at Adaptive Path have been calling Ajax. The name is shorthand for Asynchronous JavaScript + XML, and it represents a fundamental shift in what’s possible on the Web. Nog volgens deze site is Ajax geen technologie, maar een verzameling van technologie¨en die samenwerken. Deze technologie¨en worden in onderstaande lijst weergegeven. • XHTML en CSS voor presentatiedoeleinden • het Document Object Model voor dynamische weergave en interactie • XML and XSLT voor datatransfers en -manipulatie 59
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
60
• het XMLHttpRequest voor het asynchroon ophalen van data • JavaScript voor het samenwerken van de verschillende technologie¨en Bij het klassieke model van webapplicaties wordt na een gebruikersactie een HTTP-aanvraag naar de webserver verstuurd, deze server stuurt na het verwerken van de aanvraag een HTMLpagina terug. Bij het wachten op een antwoord van de server krijgt de gebruiker in de meeste gevallen een leeg scherm te zien. Een Ajax-applicatie probeert dit te vermijden door te werken met een bijkomende laag tussen de gebruiker en de server bij de cli¨ent, de zogenaamde ‘Ajax engine’. Een actie van de gebruiker wordt eerst verwerkt door de ‘Ajax Engine’ en indien nodig wordt de webserver gecontacteerd. Indien de interactie met de webserver niet vereist is, wordt de actie door de ‘Ajax engine’ afgehandeld wat resulteert in een veel snellere respons. Door te werken met Ajax is het dus niet nodig om de pagina altijd geheel te herladen na een gebruikersactie en wordt de interactiviteit van de gebruiker dus vergroot. De werkwijze bij de traditionele webpagina’s enerzijds en Ajax anderzijds wordt in figuur 6.1, die te vinden is in het eerder vermelde artikel van Jesse James Garrett, verduidelijkt.
Figuur 6.1: Het traditionele model voor een webapplicatie vergeleken met het Ajax-model
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
6.1.1
61
Ajax technieken
Het boek “Professional Ajax”, geschreven door de auteurs Nicolas C. Zakas, Jeremy McPeak en Joe Fawcet, geeft op een duidelijke manier weer hoe Ajax in de praktijk kan ge¨ımplementeerd worden. (Nicolas C. Zakas, 2006). Hiervoor zijn verschillende technieken beschikbaar. Een eerste techniek is door gebruik te maken van een HTML-frame dat verborgen wordt door de hoogte en breedte ervan gelijk te stellen aan nul. Dit verborgen frame wordt enkel gebruikt voor cli¨ent-server communicatie en wordt opgeroepen door JavaScript functies. In plaats van een gewoon HTML-frame kan ook gebruik gemaakt worden van een HTML-iframe dat ge¨ıntroduceerd werd in HTML 4.0. Dit is vergelijkbaar met een gewoon HTML-frame, maar het is nu ook mogelijk om een dergelijk iframe ‘on the fly’ toe te voegen aan een webpagina die orgineel zelfs niet ingesteld was als frameset. Deze techniek is al enkele jaren in gebruik en wordt nog steeds gebruikt in veel hedendaagse Ajax-applicaties. Het voordeel van deze techniek is dat de geschiedenis van de webpagina door de browser in de meeste gevallen perfect kan bijgehouden worden. Zo is het mogelijk om naar een vorige staat van de pagina te gaan. Een nadeel is echter dat er weinig informatie beschikbaar is over wat er gaande is achter de schermen. Wanneer er een probleem is met het laden van de pagina, dan is het moeilijk om de gebruiker daar een melding over te geven. Een andere techniek maakt gebruik van het XMLHttp-object waarlangs de communicatie tussen cli¨ent en server verloopt. Voor het cre¨eren van dit object wordt in het eerder vermelde boek (Nicolas C. Zakas, 2006) de volgende code gebruikt die in de meeste browsers kan uitgevoerd worden. var oXmlHttp = createXMLHttp(); function createXMLHttp() { if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); } else if (window.ActiveXObject) { // voor IE var aVersions = [ "MSXML2.XMLHttp.5.0","MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp","Microsoft.XMLHttp"]; for (var i = 0; i < aVersions.length; i++) { try { var oXmlHttp = new ActiveXObject(aVersions[i]); return oXmlHttp; } catch (oError) { //Do nothing } } } throw new Error("XMLHttp object could be created."); }
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
62
In deze code wordt het XMLHttp-object aangemaakt via de functie createXMLHttp(). In de browsers FireFox, Safari en Opera kan dit object aangemaakt worden via de opdracht new XMLHttpRequest(). Bij Internet Explorer moet echter een nieuw ActiveXObject aangemaakt worden wat in de tweede if uitgevoerd wordt. Van dit object zijn verschillende versies beschikbaar en de laatste versie beschikt over de beste stabiliteit en snelheid. Daarom wordt via een tabel met de versienamen getracht de recentste versie te gebruiken. Om vervolgens via het object dat we gecre¨eerd hebben een bestand “info.txt” van de server te halen, kan volgende code gebruikt worden. oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { //data is aangekomen alert("Got response."); } }; oXmlHttp.send(null); if (oXmlHttp.status == 200) { alert("Data returned is: "+ oXmlHttp.responseText; } else { alert("An error occurred: "+ oXmlHttp.statusText; }
In de methode open van het object bepalen we via de eerste parameter het aanvraagtype en in de tweede parameter wordt de naam van het gewenste bestand opgegeven. Via de laatste parameter wordt aangegeven of de aanvraag asynchroon moet verlopen (de ‘A’ van ‘Ajax’). Indien dit het geval is, loopt de uitvoering van JavaScript-code gewoon door zonder te wachten op het antwoord. Via het attribuut onreadystatechange wordt een handler toegevoegd aan het XMLHttp-object, die uitgevoerd wordt wanneer de data is aangekomen en de connectie is gesloten. Via de methode send wordt de aanvraag uiteindelijk naar de server verzonden. De gevraagde data tenslotte kan opgevraagd worden via het attribuut responseText. Via het XMLHttp-object kan ook makkelijk XML-data opgevraagd worden. Via het XMLHttp-object is het dus mogelijk om de applicatie van een degelijke foutafhandeling te voorzien, probleem is echter dat de geschiedenis van de webpagina niet kan worden bijgehouden door de browser. Daarom worden in vele Ajax-applicaties het XMLHTTP -object en de verborgen frame-techniek gecombineerd. Voor de webserver van een Ajax-applicatie zijn er niet echt grote vereisten, voor een simpele webapplicatie volstaat een webserver die dynamisch XML kan genereren. Voor het ontwikkelen van een Ajax-applicatie kan men natuurlijk ook teruggrijpen naar een bestaand framework waardoor de performantie van de applicatie kan verhoogd worden. Voor Ajax zijn verschillende frameworken beschikbaar die al dan niet het servergedeelte ook voor hun rekening nemen. Er
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
63
zijn implementaties van Ajax-frameworken in Java, .Net, PHP, C++... (Wikipedia, 2007) (Schalk, 2007) Indien we onze applicatie via Ajax realiseren, kunnen we ervoor zorgen dat de gebruiker in vele gevallen gewoon kan verderwerken bij het opvragen van data aan de server en dit zonder dat de pagina geheel herladen hoeft te worden. Het weergeven van plaatsen op een kaart vormt echter een probleem. Via HTML zou het wel mogelijk moeten zijn om bepaalde pixels volgens een bepaalde kleur in te kleuren, maar dit wordt bemoeilijkt door het feit dat dit ook door de browsers op dezelfde manier moet ondersteund worden. Daarom werd gezocht naar een technologie met meer uitgebreide grafische mogelijkheden.
6.2
Adobe Apollo
In een volgende fase werd nagegaan of de webapplicatie niet zou kunnen ontwikkeld worden met Adobe Apollo (release Alpha 1). Dit wordt door de ontwikkelaars zelf als volgt omschreven: Apollo is the code name for a new cross-operating system runtime being developed by Adobe that allows web developers to leverage their existing web development skills (such as Flash, Flex, HTML, JavaScript, and PDF) to build and deploy Rich Internet Applications and content to the desktop. De zogenaamde “Rich Internet Applications” of kortweg RIA’s zijn interactieve internetapplicaties die pogen een desktopervaring via het internet te cre¨eren. De Apollo-technologie maakt echter in strikte zin geen gebruik van de internetbrowser voor het uitvoeren van de webapplicatie. Een Apollo-project wordt gecompileerd in een AIR-bestand (dus een bestand met als extensie “.air”) en wordt zo op het internet geplaatst. Wanneer een gebruiker wil gebruik maken van de applicatie, moet hij dit bestand downloaden vanaf het internet en vervolgens uitvoeren. Om dit bestand te kunnen uitvoeren moet de gebruiker het “Apollo Runtime Environment” ge¨ınstalleerd hebben, dat kan gedownload worden op de site van Adobe. Wanneer de gebruiker het AIR-bestand uitvoert, wordt de applicatie op de computer ge¨ınstalleerd en wordt een snelkoppeling in het Start-menu van Windows geplaatst. Na deze installatie kan de gebruiker de applicatie uitvoeren zonder gebruik te maken van een internetbrowser, maar via een eigen programmavenster. Voor de weergave van HTML-code in een Apollo-programma wordt gebruik gemaakt van de open bron “Webkit”, dit is de ‘HTML-engine’ van verschillende browsers zoals KHTML via KDE en Safari via Mac OS X. Het voordeel van deze aanpak is dat de HTML-code niet hoeft getest te worden op de verschillende gangbare internetbrowsers om na te gaan of deze wel overal het gewenste resultaat geeft. Voor de ontwikkelaars van Apollo is een gratis SDK (Software Development Kit) beschikbaar met onder meer ‘commandline tools’ voor het compileren van de projecten. De projecten
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
64
kunnen ook ontwikkeld worden via het door Adobe geleverde Flex Builder-programma dat geleverd wordt met debugging-functies en een aangepaste visuele layout wat niet het geval is voor de SDK. Met Apollo wordt er voornamelijk gecodeerd in de MXML-syntax, die ook wordt gebruikt bij Adobe Flex 2. Via Flex 2 is het ook mogelijk om RIA’s te ontwikkelen, deze RIA’s worden dan via Flash Player in de internetbrowser afgespeeld. Flex 2 wordt uitvoerig in hoofdstuk 6.3 behandeld. In een Apollo-project kunnen, zoals reeds aangegeven, componenten van verschillende technologie¨en opgenomen worden, zo is het bijvoorbeeld mogelijk om de API van Flex en Flash aan te spreken. Er is ook een Apollo-specifieke API ontwikkeld die de mogelijkheden van een applicatie zonder tussenkomst van een internetbrowser ten volle benut. E´en van deze extra functionaliteiten is ‘rich drag-and-drop support’ en dat zowel tussen een Apollo-applicatie en het besturingssysteem als tussen Apollo-applicaties onderling. De mogelijkheid is ook voorzien om de applicatie offline te laten werken via datacaching en er is een API voorzien voor het opslaan van bestanden en benaderen van het bestandssysteem. Apollo biedt dus veel nieuwe mogelijkheden. Het loont dan ook de moeite om een bezoekje te brengen aan de website van Apollo waar samples van Apollo-projecten te downloaden zijn. (Labs, 2007) Voor ons project is vooral de mogelijkheid om met Flex te werken erg interessant. Flex 2 heeft namelijk uitgebreide grafische mogelijkheden en biedt ook de mogelijkheid om crossbrowser te werken maar dan via Flash Player. De applicatie ontwikkelen met Apollo heeft echter ook enkele nadelen. Tijdens het vooronderzoek naar de technologie voor de webapplicatie is Apollo nog steeds in een ‘pre-release’ fase en hierdoor zijn nog niet alle beschikbare API’s beschikbaar en foutvrij. Voorlopig kan een Apollo-programma ook nog niet uitgevoerd worden met het Linux-besturingssyteem. De extra functionaliteiten die Apollo biedt, zijn ook niet echt vereist voor de ontwikkeling van onze applicatie. Hetgeen Apollo voor ons vooral interessant maakt is de mogelijk om met Flex te werken, daarom is het misschien beter om eens de mogelijkheden van Flex te evalueren, wat gebeurt in het volgende hoofdstuk. Apollo heeft sinds 10 juni 2007 de nieuwe naam AIR of ‘Adobe Integrated Runtime ’ meegekregen maar dit is nog steeds een ‘pre-release’-Beta versie. De AIR 1.0 versie is gepland in voor de tweede helft van het jaar 2007.
6.3
Adobe Flex 2
Adobe Flex 2 is een framework waarmee ‘Rich Internet Applications’ kunnen ontwikkeld worden. De term RIA’s zijn we ook al tegengekomen bij de bespreking van Apollo. We kunnen hieraan nog toevoegen dat de term RIA voor het eerst werd ge¨ıntroduceerd in maart 2002 door Macromedia (ondertussen is dit bedrijf overgenomen door Adobe en draagt het ook deze naam) waar het als volgt beschreven werd:
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
65
Rich Internet Application (RIA) are these new types of applications in browsers, which blend content, application logic and communication...to make the Internet more usable and enjoyable. Een Flex-webapplicatie wordt afgespeeld via Adobe Flash Player en hierdoor is de technologie crossbrowser en systeemonafhankelijk. De Flex productlijn bestaat uit verschillende technologie¨en, namelijk: • Adobe Flex Builder 2 • Adobe Flex 2 SDK • Adobe Flex Data Services 2 • Adobe Flex Charting 2 Flex Builder is de IDE (Integrated Development Environment) waarmee de webapplicaties kunnen ontwikkeld worden. De Flex 2 SDK is de basisset van technologie¨en en benodigdheden die gebruikt worden voor de ontwikkeling. Via deze SDK is het mogelijk om via een gewoon tekstprogramma de broncode voor flex te schrijven en vervolgens te compileren via commandline-tools. Dit is echter niet aan te raden aangezien men via Flex Builder beschikt over een aangepaste interface voor Flex en ruime debugfuncties. Nadeel is wel dat Flex Builder niet gratis is, wat wel het geval is voor de Flex 2 SDK. De functionaliteit van de Flex 2 SDK kan uitgebreid worden met Flex Data Services 2 waardoor de mogelijkheden aan de serverkant sterk uitgebreid worden, zo verbetert de synchronisatie tussen cli¨ent en server en is ‘data streaming’ mogelijk. Deze extra mogelijkheden zijn echter niet vereist voor ons project en de prijs voor een licentie van Data Services is nogal hoog. Adobe Flex Charting 2 maakt het mogelijk om makkelijk data weer te geven via tabellen en grafieken, maar dit is niet interessant voor ons project.
6.3.1
Stuctuur Flex applicatie
Een Flex applicatie is opgebouwd uit ´e´en of meerdere MXML-bestanden die onder meer de layout van de applicatie bepalen. MXML is een zogenaamde Markup-taal en is een implementatie van XML, speciaal ontworpen voor het cre¨eren van Flex applicaties. In Flex wordt ook ActionScript 3.0 gebruikt en dit vooral voor het opvangen van gebeurtenissen. ActionScript 3.0 is een implementatie van ECMAScript en is vergelijkbaar met JavaScript en is een taal waarin objectgericht kan geprogrammeerd worden. ActionScript kan toegevoegd worden via
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
66
scriptblokken in de MXML-bestanden of kan ook geplaatst worden in aparte bestanden die dan ge¨ımporteerd worden in de MXML-bestanden. Via CSS kunnen de stijleigenschappen van visuele componenten zoals knoppen,lijsten,... ingesteld worden. Bij het compileren van een Flex project wordt voor ieder MXML-bestand een ActionScriptklasse gegenereerd. Deze ActionScript-klassen worden vervolgens gecompileerd in ´e´en enkel SWF-bestand (Small Web Format), dat kan afgespeeld worden met Flash Player. Alle afbeeldingen en eventuele andere objecten die gebruikt worden bij uitvoering worden ook aan dat bestand toegevoegd. Dit SWF-bestand wordt vervolgens op de webserver geplaatst. Het compileerproces is ge¨ıllustreerd in volgende figuur. De “Flex Class Library Components” die op de figuur te zien zijn, bevatten de klassen van het Flex Framework in SWC-bestanden. Wanneer een cli¨ent wenst gebruik te maken van de applicatie, wordt het SWF-bestand van de
Figuur 6.2: Het compileerproces
server gehaald en vervolgens uitgevoerd op de cli¨ent. Dit uitvoeren gebeurt door Flash Player (voor een Flex 2 is Adobe Flash Player 9 vereist) via de ingebouwde “ActionScript Virtual Machine”, het voordeel van deze werkwijze is de snelheid aangezien alles aan “cli¨entside” gebeurt. Een ander voordeel is dat het op bijna iedere computer aangesloten op het internet afspeelbaar is. Flash Player 8 was namelijk het meest via internet verspreide stuk software in de computergeschiedenis en via de autoupdate-functie is de verspreiding van Flash Player 8 ook erg goed (Talbot, 2006). De data die weergegeven wordt in de applicatie, eventueel na een actie van de gebruiker, kan op verschillende manieren opgehaald worden. Dit wordt besproken in de volgende paragraaf.
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
6.3.2
67
Datatoegang
Eenmaal de applicatie in uitvoering is op de cli¨ent kan er nog data opgevraagd worden via HTTPServices, Flex DataServices of Webservices. Bij HTTPServices wordt de data opgehaald via de gebruikelijke HTTP GET - en POST -methodes. Flex DataServices ondersteunt verschillende methodes van toegang, maar is wegens eerder genoemde redenen voor ons niet interessant. Bij WebServices wordt gebruik gemaakt van SOAP-berichten en dit biedt veel mogelijkheden en zou voor dit project interessant kunnen zijn. Indien geen gebruik gemaakt wordt van Flex DataServices, worden geen specifieke eisen aan de webserver gesteld. Wanneer men gebruik maakt van webservices moet dit natuurlijk wel door de webserver ondersteund worden. De webapplicatie moet zoals eerder gezien in staat zijn om op een kaartafbeelding te tekenen. Via Flex is dit zeker mogelijk aangezien men ook kan gebruik maken van de Flash-bibliotheken. Deze bibliotheken kunnen via ActionScript-code aangesproken worden in ActionScript-componenten die kunnen ge¨ıntegreerd worden in het Flex-project. Een Flex-webapplicatie kan geconstrueerd worden volgens de Model-View-Controller architectuur of kortweg MVC. Via deze benadering worden datacomponenten (Model, bijvoorbeeld een Array) gekoppeld View-objecten (bijvoorbeeld een DataGrid ) en een Controller die instaat voor het verwerken van de gebeurtenissen. De datacomponenten kunnen echter meer informatie bevatten dan op een gegeven ogenblik getoond wordt. Hierdoor is het mogelijk om te reageren op gebeurtenissen en vervolgens nieuwe data te tonen, zonder nieuwe data te hoeven ophalen van de webserver, dit verhoogt dus de snelheid en interactiviteit. We kunnen besluiten dat Flex 2 een technologie is die zeker aanvaardbaar is voor de uitvoering van ons project.
6.4
Conclusie
Voor de ontwikkeling van de webapplicatie wordt best gebruik gemaakt van Flex 2. Deze technologie heeft namelijk veel geavanceerde grafische mogelijkheden die we kunnen gebruiken voor het tekenen van plaatsen op de kaarten. Flex 2 biedt veel mogelijkheden om met een webservice te werken, daarom zal de data aan de applicatie geleverd worden door een webservice. Aangezien de webserver Java ondersteunt, zal de webservice met deze taal worden ontwikkeld. Wanneer een gebruiker de website bezoekt, wordt eerst het SWF-bestand van de webserver gehaald en wordt de benodigde data uit de databank vervolgens via de webservice geleverd. Uit de volgende hoofdstukken zal blijken dat het aangewezen formaat voor de communicatie tussen de webserver en de webapplicatie XML is. De communicatie tussen de verschillende technologie¨en is ge¨ıllustreerd in figuur 6.3.
Hoofdstuk 6. Technologieverkenning voor de webapplicatie
Figuur 6.3: De communicatie tussen de verschillende technologie¨en
68
HOOFDSTUK
7
De webservice
De Flex-webapplicatie haalt zoals eerder gezien de benodigde informatie op via een webservice. Het is de bedoeling om deze webservice te ontplooien op de “Amerigo”-server van Hogeschool Gent. Op deze Linux-server is het Java-platform reeds ge¨ınstalleerd zodat Java-applicaties kunnen gedraaid worden. Als webcontainer is Apache Tomcat voorhanden en het is dan ook wenselijk om de webservice hiermee op te zetten.
7.1
Het webservice-concept
Een webservice wordt door W3C (World Wide Web Consortium) als volgt gedefinieerd: (W3C, 2007) A Web service is a software system designed to support interoperable machine-tomachine interaction over a network. It has an interface described in a machineprocessable format (specifically WSDL). Other systems interact with the Web service in a manner prescribed by its description using SOAP-messages, typically conveyed using HTTP with an XML serialization in conjunction with other Webrelated standards. Een webservice biedt een dienst aan die kan aangevraagd worden, deze dienst is meestal het uitvoeren van een of meerdere methodes op de server. Een van de voordelen die webservices bieden is dat er geen platformspecifieke vereisten zijn voor de aanvrager van een dienst. De berichten tussen de webservice en de cli¨ent zijn in het SOAP-formaat. Dit formaat structureert het bericht via XML en steekt het in een zogenaamde “Envelope”-tag. De methodes die door een webservice worden aangeboden, worden aan de buitenwereld getoond via WSDL (Webservice Description Language). Dit is een soort van contract tussen de webservice en de 69
Hoofdstuk 7. De webservice
70
cli¨ents. Op basis van deze WSDL is de cli¨entapplicatie in staat een zogenaamd proxy-object aan te maken, dit is een lokaal object dat de webservice voorstelt. De cli¨ent hoeft vervolgens enkel de methodes van het proxy-object aan te roepen. Dit wordt ge¨ıllustreerd in figuur 7.1. (Ongenae, 2006)
Figuur 7.1: De cli¨ent-webserver communicatie
Wanneer een cli¨ent een bericht naar de server stuurt wordt dit eerst vertaald naar een SOAPbericht, het zogenaamde serialiseren. Bij de webservice wordt dit SOAP-bericht terug omgezet, het bericht wordt gedeserialiseerd. Bij communicatie in de omgekeerde richting wordt er door de webservice eerst geserialiseerd, waarna er door de cli¨ent terug gedeserialiseerd wordt. Om een webservice te realiseren zijn verschillende technologie¨en beschikbaar. Er is de Microsoft .NET-technologie, maar deze is voor ons niet aangewezen aangezien we een linuxserver ter beschikking hebben. Daarom wordt de webservice in Java ontwikkeld.
7.2
De JAX-WS-webservice
De webservice wordt ontwikkeld door gebruik te maken van JAX-WS 2.0 (Java API for XML Web Services) onderdeel van het Java EE 5 platform (Java Platform, Enterprise Edition). Dit is de opvolger van JAX-RPC 1.1 (Java API for XML-based RPC). JAX-WS zorgt volgens Sun voor een makkelijkere ontwikkeling dan zijn voorganger. (Netbeans tutorial, 2007) (Articles Java - Bharath Mundlapudi, 2007) De webservice via JAX-WS wordt ontwikkeld met NetBeans 5.5. Hiertoe moet er nieuw webapplicatie-project aangemaakt worden en moet er gekozen worden van welke soort server de webservice gebruik gaat maken. Er is keuze tussen de Sun Application Server en de Tomcat Webserver. We kiezen voor de Tomcat webserver, die beschikbaar is op de “Amerigo”-server. Dit heeft ook wel enkele nadelen die we later zullen bespreken. Vervolgens kan aan de webap-
Hoofdstuk 7. De webservice
71
plicatie een webservice toegevoegd worden. In volgende code wordt een webservice getoond die de String “kaart” teruggeeft. import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; @WebService() public class CriminaliteitWS { /** * Web service operation */ @WebMethod public String getKaartString() { return "kaart"; } } Hetgeen bij deze code opvalt zijn de aanduidingen “@WebService” en “@WebMethod” die beschreven staan in “JSR 181: Web Services Metadata for the Java Platform” (JSR staat voor “Java Specification Requests”) (Java Community Proces, 2007b). “@WebService” specificeert een klasse van een webservice (javax.jws.WebService) of een interface die een webservice definieert. “@WebMethod” wordt gebruikt voor een methode die gepubliceerd wordt als een webservice-operatie. Deze methode moet bijgevolg “public” gedeclareerd worden, anders wordt er een fout opgeworpen. Aangezien dit een webapplicatie is, moet het bestand “web.xml” ingevuld worden, dit wordt automatisch door Netbeans gedaan. In dit bestand vindt men onder meer de Servlet-klassen die moeten opgeroepen worden bij een bepaalde URL, de webservice wordt namelijk uitgevoerd als een Java-Servlet. Het project kan nu gecompileerd en uitgevoerd worden. Wanneer men tijdens uitvoering naar de volgende URL surft krijgt men de WSDL van de webservice te zien (eventueel moet het poortnummer nog veranderd worden naar de poort waarop de Tomcat server draait). http://localhost:8084/Projectnaam/CriminaliteitWS?wsdl Het is een algemene regel bij webservices om de WSDL terug te geven wanneer men aan de URL van de webservice “?wsdl” toevoegt. In de WSDL wordt onder meer de methode getKaartString met bijbehorende parameter beschreven, dit kan gebruikt worden voor cli¨entapplicaties die gebruik willen maken van de webservice. Een cli¨entapplicatie die gebruik maakt van de webservice kan ook makkelijk ontwikkeld worden via Netbeans met behulp van de WSDL.
Hoofdstuk 7. De webservice
72
Bij het testen van de webservice maken we geen gebruik van een Java-cli¨entapplicatie maar van het programma “Altova XMLSPY”. Met dit programma is het mogelijk om via de WSDL een SOAP-aanvraag op te stellen voor een bepaalde methode van de webservice. Vervolgens kan men deze aanvraag versturen naar de webservice en het daaropvolgende SOAP-antwoord bekijken. Via deze aanpak kan snel getest worden of de webservice het gewenste resultaat teruggeeft en er is nog een bijkomend voordeel. Het gebruikte programma werkt namelijk onafhankelijk van Java zoals ook onze webapplicatie in Flex zal doen. Eventuele conversies tussen objecten die tussen twee Java-programma’s geen problemen zouden geven, kunnen dat bij een cli¨entapplicatie onafhankelijk van Java wel doen en worden door deze aanpak ook gedetecteerd. Een webservice met JAX-WS kan, zoals eerder gezegd, opgezet worden via de eigen Application Server van Sun , de “Sun Java System Application Server”, die veel mogelijkheden biedt en waar ook veel documentatie over te vinden is. Wanneer men echter de Tomcat webserver gebruikt zijn de mogelijkheden beperkter, zo kan men geen gebruik maken van EJB (Enterprise JavaBeans) of van webservices volgens JSR-109 (“Enterprise Webservices”) (Netbeans tutorial, 2007) (Java Community Proces, 2007a). Ook is documentatie over de meer geavanceerdere aspecten van webservices opgezet via een Tomcat webserver moeilijk te vinden. Voor het serialiseren van de Java-objecten naar een SOAP-bericht wordt bijvoorbeeld gebruikt gemaakt van JAX-B (Java Architecture for XML Binding) (Articles Java - Ed Ort, 2007). Over JAX-B in combinatie met de Sun Application Server is veel documentatie vinden, ook met de GlassFish Application Server is dit het geval. (GlassFish is onder meer een open source Application Server die de nieuwigheden in het Java EE 5 platform implementeert en is ook de naam voor het gelijkaardige project/community die dit realiseert (GlassFish, 2007) ) Wanneer men echter informatie zoekt over JAX-B in combinatie met de Tomcat webserver is er weinig te vinden. Nochtans moet er wel kunnen gespecificeerd worden hoe eigengemaakte klassen kunnen omgezet worden naar XML. Bij de webservice-operatie getKaartString gebeurde dit automatisch aangezien het return-object een Java-String-object is, maar met eigengemaakte klassen zoals Plaats of Kaart blijkt dit niet het geval te zijn. Daarom is de ontwikkeling van de webservice via JAX-WS stopgezet en is de webservice verder ontwikkeld via Apache Axis.
7.3
De AXIS-webservice
Apache Axis (Apache EXtensible Interaction System) is een populair open source framework voor webservices of om het met de woorden van de makers zelf te zeggen: (Apache Axis, 2007) (Wikipedia, 2007) Axis is essentially a SOAP engine – a framework for constructing SOAP processors such as clients, servers, gateways, etc.
Hoofdstuk 7. De webservice
73
Axis is echter niet beperkt tot enkel een “SOAP engine”, maar heeft ook veel mogelijkheden met betrekking tot WSDL en het extraheren van Java-klassen uit de WSDL. We gebruiken versie 1.4 van Axis ge¨ımplementeerd in Java. (Er is ook een versie ge¨ımplementeerd in C++ beschikbaar). De versies 1.x van Axis implementeren de JAX-RPC API en de versies 2.x implementeren de JAX-WS API. De 1.4-versie zal voor ons zeker volstaan en is tot op heden veel meer gebruikt en dus ook stabiel in vergelijking met de relatief recente 2.x-versies.
7.3.1
De ontwikkeling van de webservice
Voor het ontwikkelen van deze webservice kan terug gebruik gemaakt worden van Netbeans, maar nu worden er minder instellingen automatisch door Netbeans ingevuld. Eerst wordt terug een Webapplicatie-project aangemaakt en vervolgens moeten enkele jar -bestanden toegevoegd worden aan de “Library” van het project. Deze jar -bestanden kunnen gedownload worden vanaf de Axis-website en bevatten de class-files die instaan voor de service die door Axis geleverd wordt. Deze Axis-service wordt namelijk ge¨ımplementeerd als een Java Servlet. Aangezien de Axis-service als een servlet opgeroepen wordt, moet dit aangegeven worden in het “web.xml”-bestand. In de hierop volgende code wordt een stukje uit dit bestand getoond. <servlet-name>AxisServlet
Apache-Axis Servlet <servlet-class>org.apache.axis.transport.http.AxisServlet <servlet-mapping> <servlet-name>AxisServlet
/criminaliteitws Via de servlet-mapping en servlet-tags kan men de klasse specificeren die moet opgeroepen worden als Java Servlet bij een bepaalde URL. De klasse “org.apache.axis.transport.http.AxisServlet” is te vinden in ´e´en van de jar -bestanden die we toegevoegd hebben aan het project. Nu hoeven we enkel nog de eigenlijke webservice die moet opgeroepen worden door Axis toe te voegen aan het project. Dit kan op 2 manieren gedaan worden, via het zogenaamde “Instant Deployment” of via “Custom Deployment”. Wanneer men werkt met “Instant Deployment” is er weinig werk te verrichten, maar deze werkwijze geldt enkel voor erg simpele webservices. De klassen worden dan op het moment van uitvoering gecompileerd en er kan ook niet gewerkt worden met Java-pakketten. Voor het ontwikkelen van onze webservice wordt gebruik gemaakt van de “Custom Deployment”-werkwijze.
Hoofdstuk 7. De webservice 7.3.1.1
74
Custom Deployment
Eerst wordt de Javaklasse van de webservice toegevoegd aan het project. Deze klasse met bijbehorende interface ziet er voorlopig nog als volgt uit: package be.hogent.iii.criminaliteit.web.ws; public interface CriminaliteitWS extends java.rmi.Remote { public String geefString()throws java.rmi.RemoteException; } package be.hogent.iii.criminaliteit.web.ws; public class CriminaliteitWSImpl implements CriminaliteitWS{ /** * Creates a new instance of CriminaliteitWSImpl */ public CriminaliteitWSImpl() throws java.rmi.RemoteException {} public String geefString()throws java.rmi.RemoteException{ return "een string"; } } De klasse CriminaliteitWS heeft ´e´en methode,geefString die een String-object teruggeeft. Deze klasse moet nu nog gekoppeld worden aan de Axis-service en dit gebeurt via de “Axis Web Service Deployment Descriptor” of kortweg “WSDD”. Dit is een XML-bestand dat de webservice beschrijft en hiermee kunnen onder meer operaties, parameters en namespaces van de webservice ingesteld worden. De belangrijkste tags voor onze webservice worden in de hierop volgende code getoond. <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="CriminaliteitWS" use="literal" style="wrapped" provider="java:RPC"> <parameter name="wsdlTargetNamespace" value="http://ws.web.criminaliteit.iii.hogent.be"/> <parameter name="wsdlServiceElement" value="CriminaliteitWS"/> <parameter name="wsdlServicePort" value="CriminaliteitWSPort"/> <parameter name="className" value="be.hogent.iii.criminaliteit.web.ws.CriminaliteitWSImpl"/> <parameter name="wsdlPortType" value="CriminaliteitWSPortType"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="Session"/>
Hoofdstuk 7. De webservice
75
De buitenste tag geeft aan dat dit bestand een “WSDD-deployment” betreft en definieert onder andere de “java”-namespace. Voor iedere webservice is er een service-tag en de instellingen voor deze webservice worden ingesteld via de parameter -tags. Het style-item in de servicetag bepaalt hoe de SOAP-berichten opgebouwd moeten worden, andere mogelijkheden dan “wrapped” voor dit item zijn “RPC”,“Document” en “Message”. De parameter classname bepaalt waar de webservice-klasse kan gevonden worden, het is opmerkelijk dat men hier de implementatieklasse moet opgeven en niet de interface. Via scope kan ingesteld worden wanneer een nieuw service-object moet worden aangemaakt. In ons geval wordt een nieuw object aangemaakt bij iedere nieuwe sessie, andere mogelijkheden zijn “Request” en “Application”. Via de parameter allowedMethods kan ingesteld worden welke operaties van de webservice beschikbaar zijn. In ons geval is dit iedere methode te vinden in de webservice aangegeven door het “*”-teken. De overige parameters bepalen hoe de webservice naar de buitenwereld toe gedefinieerd wordt en worden ook opgenomen in de WSDL. Dit bestand moet in de map “WEB-INF” geplaatst worden (waar ook het bestand “web.xml” te vinden is) en krijgt als naam “server-config.wsdd”. Nu is axis in staat om de webservice met bijbehorende methode te lokaliseren en kan het project gecompileerd en uitgevoerd worden. Wanneer we vervolgens terug naar het volgende adres gaan, krijgen we de WSDL te zien (Het project heeft als naam CriminaliteitWS). http://localhost:8084/CriminaliteitWS/criminaliteitws?wsdl Vervolgens kunnen we via “Altova XMLSPY” een SOAP-aanvraag voor de webservice genereren die er als volgt uitziet: <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Body> <m:geefString xmlns:m="http://ws.web.criminaliteit.iii.hogent.be"/> Het SOAP-antwoord ziet er dan als volgt uit (met weglating van de namespaces):
Hoofdstuk 7. De webservice
76
<soapenv:Envelop ...> <soapenv:Body>
een string De webservice geeft dus het gewenste String-object terug. Om de webservice rechtstreeks via de Tomcat webserver te laten uitvoeren, moet het war -bestand van het project in de desbetreffende webcontainer geplaatst worden. 7.3.1.2
WSDD opties
In de WSDD kan men expliciet aangeven welke operaties de webservice bevat en welke instellingen daarvoor gelden. Per methode van de webservice wordt een operation-tag in de service-tag geplaatst. Het qname-item bepaalt de naam van de operatie in de WSDL en de SOAP-aanvragen. In de operation-tag kan ook informatie over de parameters van de methode geplaatst worden. Dit wordt in de volgende code ge¨ıllustreerd.
In het vorige voorbeeld hebben we deze tag echter niet toegevoegd aan de WSDD aangezien dit automatisch door Axis gebeurt, in dat geval neemt Axis gewoon de desbetreffende methodenaam en de namespace van de webservice. In het voorgaande voorbeeld werd een String als parameter doorgegeven en aangezien dit een standaard Java-klasse is, wordt dit automatisch door Axis omgezet naar het XML-type “xsd:string”, zoals bepaald door de JAX-RPC specificatie. Wanneer we gebruik maken van zelfgemaakte klassen als parameters moet in de WSDD aangegeven worden hoe de serialisatie en deserialisatie moet gebeuren. Om dit aan te tonen breiden we de webservice uit met een methode zoekPlaatsen die een tabel van plaatsen teruggeeft zoals in onderstaande code. public Plaats[] zoekPlaatsen(String naam,String nr, String actuelenaam,String type)throws java.rmi.RemoteException{ Plaats[] plaatsen=null; try{ ArrayList plaatsenlist=accessdb.zoekPlaatsen(naam,nr,actuelenaam,type);
Hoofdstuk 7. De webservice
77
plaatsen=(Plaats[])plaatsenlist.toArray(new Plaats[plaatsenlist.size()]); } catch(Exception ex){ throw new RemoteException(" ... fout met het benaderen van de databank " + ex.getMessage()); } return plaatsen; }
Deze methode benadert de databank op dezelfde manier zoals bij de kaartenapplicatie het geval was en dus via de AccessDb-klasse (zie hoofdstuk 5). Indien een fout optreedt, wordt een java.rmi.RemoteException opgeworpen die bij de serialisatie omgezet wordt naar een “SOAP Fault”. Dit is beter dan het opwerpen van een gewone Exception aangezien deze laatste naar een gewone Java-klasse omgezet wordt zonder extra mogelijkheden. Aangezien we werken met een eigengemaakte klasse Plaats moet de volgende tag aan de service-tag van de WSDD toegevoegd worden. (De volledige WSDD kan gevonden worden in de appendix E )
Via deze tag wordt aangegeven dat bij het omzetten van een Java-object Plaats naar XML gebruik moet gemaakt worden van de Java-BeanSerializerFactory-klasse. Het omzetten van een Java-object naar XML wordt “marshalling” genoemd, het omgekeerde proces “unmarshalling” en bij deze processen worden de get- en setmethodes van de desbetreffende klasse gebruikt. Verder wordt in deze tag ook de Java-klasse voor het “unmarshalling”-proces opgegeven en via qname kan men de naam opgeven voor de Plaats-klasse in de WSDL. Wanneer we nu vervolgens het project compileren en uitvoeren kunnen we via de gebruikelijke manier de WSDL opvragen,waarvan de belangrijkste tags hieronder getoond zijn (De volledige WSDL kan gevonden worden in de appendix E ) <wsdl:definitions targetNamespace="http://ws.web.criminaliteit.iii.hogent.be"> <wsdl:types> <schema elementFormDefault="qualified" ...>... <element name="zoekPlaatsen">... <element name="zoekPlaatsenResponse">
<sequence> <element maxOccurs="unbounded" name="zoekPlaatsenReturn" type="impl:Plaats"/>
Hoofdstuk 7. De webservice
78
<sequence> <element name="actueleNaam" nillable="true" type="xsd:string"/> <element name="naam" nillable="true" type="xsd:string"/> <element name="nr" nillable="true" type="xsd:string"/> <element name="type" nillable="true" type="xsd:string"/> <wsdl:message name="zoekPlaatsenResponse"> <wsdl:part element="impl:zoekPlaatsenResponse" name="parameters"/> <wsdl:message name="zoekPlaatsenRequest"> <wsdl:part element="impl:zoekPlaatsen" name="parameters"/> <wsdl:portType name="CriminaliteitWSPortType"> <wsdl:operation name="zoekPlaatsen"> <wsdl:input message="impl:zoekPlaatsenRequest" name="zoekPlaatsenRequest"/> <wsdl:output message="impl:zoekPlaatsenResponse" name="zoekPlaatsenResponse"/> <wsdl:service name="CriminaliteitWS"> <wsdl:port binding="impl:CriminaliteitWSPortSoapBinding" name="CriminaliteitWSPort">
Via de operation- en portType-tag is gedefinieerd dat deze service een zoekPlaatsen-methode bevat. De zoekPlaatsenRequest- en zoekPlaatsenResponse-items bepalen welke parameters er bij een SOAP-bericht gebruikt worden. Bij het zoekPlaatsenResponse-item ziet men dat er als antwoord een sequence of opeenvolging van zoekPlaatsenReturn-objecten gegeven wordt met als type impl:Plaats. Wanneer we echter een ArrayList van Plaats-objecten zouden hebben doorgegeven als parameter van de methode zou er als type van het zoekPlaatsenReturn-object xsd:anyType gestaan hebben. Bij een ArrayList weet men namelijk vooraf niet welk type objecten het zal bevatten, daarom is het beter dergelijke lijsten om te zetten naar een tabel van het desbetreffende type. Bij de defini¨ering van het type Plaats wordt bij de elementen ervan het item nillable op “true” gezet, dit duidt aan dat het element gelijk kan zijn aan null. Wanneer we vervolgens op basis van deze WSDL een SOAP-aanvraag met ingevulde parameters versturen krijgen we een SOAP-antwoord terug waarvan een gedeelte hieronder getoond wordt. <soapenv:Body>
Kortrijk Kortrijk Kasselrij
Hoofdstuk 7. De webservice
79
Brugge Kasselrij In dit SOAP-antwoord bestaat het zoekPlaatsenResponse-item uit een opeenvolging van zoekPlaatsenReturn-items zoals aangegeven is in de WSDL. Om het verwerken van de SOAP-berichten in de webapplicatie te vergemakkelijken zou het handiger zijn dat iedere Plaats, die teruggeven wordt in het SOAP-bericht ook vergezeld wordt van een Plaats-tag. In het bovenstaande voorbeeld zou iedere zoekPlaatsenReturn-tag dus vervangen worden door een Plaats-tag. Dit kan verwezenlijkt worden door aan de WSDD een operation-tag met parameter -tags toe te voegen en te benoemen. Dit wordt ge¨ıllustreerd in volgende code:
<parameter name="naam" qname="ns:naam" type="xsd:string" mode="IN"/> <parameter name="nr" qname="ns:nr" type="xsd:integer" mode="IN"/> <parameter name="actuelenaam" qname="ns:actuelenaam" type="xsd:string" mode="IN"/> <parameter name="type" qname="ns:type" type="xsd:string" mode="IN"/> <parameter name="typeselected" qname="ns:typeselected" type="xsd:boolean" mode="IN"/>
Via deze tag wordt bepaald dat er een methode zoekPlaatsen is, gekoppeld aan de reeds gebruikte namespace. Via returnType en returnQname wordt aangegeven dat er plaatsen teruggeven worden en de waarden hiervoor verwijzen naar de reeds aanwezige typemappingtag voor Plaats. Verder worden ook de inputparameters in de operation-tag aangegeven. Het SOAP-antwoord ziet er nu als volgt uit: <soapenv:Envelope ...xmlns:xsi="...."’ ...> <soapenv:Body>
Hoofdstuk 7. De webservice
80
Brugge: Boterhuis en botermarkt onbepaald galg buiten de stad onbepaald De WSDL voor de webservice is bijgevolg ook veranderd en is toegevoegd aan de appendix E Voor het ophalen van de overige objecten uit de databank door de webservice zijn vergelijkbare methodes voorzien die instaan voor het ophalen van de kaarten, vonnissen, archiefbronnen,...
HOOFDSTUK
8
De webapplicatie ontwikkeld met Flex 2
De webapplicatie wordt ontwikkeld via Flex zoals aangegeven in hoofdstuk 6.4. Deze applicatie krijgt de benodigde data uit de databank geleverd via de webservice die beschreven is in hoofdstuk 7. De applicatie wordt ontwikkeld via het eerder genoemde programma Flex Builder 2 voor software-ontwikkeling in Flex. Bij Flex Builder is een aangepaste versie van Flash Player geleverd dat over debugging-functies beschikt. Wanneer er zich een fout voordoet tijdens de uitvoering van de Flex-applicatie met de aangepaste Flash Player, krijgt men te zien wat de fout precies is en ook op welke lijn van de code die fout zich heeft voorgedaan.
8.1
Voorstelling applicatie
Via deze applicatie kunnen vonnissen uit de databank opgevraagd worden. De vonnissen worden opgezocht via bijvoorbeeld de plaats van het misdrijf, het type ervan, de periode,... waarvoor dan zoektermen kunnen ingegeven of geselecteerd worden. Vervolgens worden de belangrijkste velden van het vonnis in een tabel getoond. In deze tabel kan een vonnis geselecteerd worden door er met de muis op te klikken, waardoor er op de knoppen naast de tabel kan geklikt worden. Via deze knoppen kan men verdere gedetailleerde informatie over het geselecteerde vonnis krijgen via een popupscherm dat informatie weergeeft over bronnen, verdere details van het vonnis, betrokken personen... Het is ook mogelijk om de kaarten op te vragen die aan de databank toegevoegd zijn via de kaartenapplicatie (zie hoofdstuk 5 ). Op deze kaarten worden de aan de kaart gekoppelde plaatsen via cirkels weergegeven en wanneer men op deze plaats klikt, wordt verdere informatie over deze plaats getoond.
81
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
8.2
82
Programmastructuur
Zoals eerder aangegeven, worden alle MXLM-bestanden gecompileerd tot ´e´en enkel SWFbestand dat vervolgens op de server wordt geplaatst. In een Flex-project is er altijd ´e´en hoofd-MXML-bestand waarmee de applicatie gestart wordt. Vanuit dit bestand worden de andere MXML-bestanden opgeroepen. Het is aangewezen om de diverse componenten van de applicatie in aparte MXML-bestanden op te slaan aangezien de code hierdoor veel overzichtelijker wordt. Deze MXML-bestanden kunnen in verschillende mappen ingedeeld worden volgens hun functie. De mappenstructuur van het project wordt getoond in figuur 8.1. Het hoofd-MXML-bestand wordt in de projectmap criminaliteit geplaatst. In de map views
Figuur 8.1: De mappenstructuur van het project
vindt men de Flex-containers die de weergave van de applicatie bepalen bij een bepaalde keuze. Voorbeelden hiervan zijn ZoekWindow (via dit scherm kan men vonnissen opzoeken) of KaartWindow waarmee kaarten weergegeven kunnen worden. De MXML-bestanden van de popupschermen met gedetailleerde informatie over de vonnissen worden ook in deze map geplaatst. De Flex-componenten of subonderdelen die gebruikt worden in een object van de views-map of in het hoofdbestand worden geplaatst in de map components. De tabel die bijvoorbeeld getoond wordt via het ZoekWindow -object is gerealiseerd door een Flex- DataGrid -object en dit wordt dan in de map components geplaatst. Aangezien in Flex objectgericht geprogrammeerd kan worden, kan de bestaande Event-klasse uitgebreid worden. Hierdoor is het bijvoorbeeld mogelijk om bij selectie van een vonnis uit de tabel een zelfgemaakte Event of gebeurtenis te laten genereren en te laten opvangen door de applicatie. Deze zelfgemaakte Event-klasse kan dan in de map events worden geplaatst. In de map assets tenslotte worden hulpbestanden geplaatst, zoals kleine afbeeldingen gebruikt in de applicatie. Via ActionScript is het mogelijk om voor alle objecten in onze applicatie zoals vonnissen, kaarten, archiefbronnen,... een klasse te maken. De objecten van deze klassen kunnen gekoppeld
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
83
worden aan de containers die instaan voor de weergave van de data zoals het DataGridobject. In dit project is er echter voor gekozen om dit niet te doen. Flex geeft namelijk een erg goede ondersteuning voor XML en het is zelfs mogelijk om XML als een “native datatype” te behandelen. Hiermee wordt bedoeld dat men XML kan behandelen als een gewoon datatype, dus op dezelfde manier zoals een String-object behandeld zou worden. Deze wijde XML-ondersteuning wordt E4X of “ECMAScript for XML” genoemd. De data gebruikt in onze applicatie gaan we dus bijhouden in XML-objecten. De data die we van de webservice toegestuurd krijgen is ook in XML-formaat en kan dus zonder conversie in onze applicatie gebruikt worden. Het hoofdbestand voor onze applicatie is “criminaliteit.mxml”. Dit bestand bestaat uit ´e´en hoofd-XML-tag Application voor de gehele applicatie. Componenten kunnen aan de applicatie toegevoegd worden door de desbetreffende XML-tag in de Application-tag te plaatsen. Dit wordt ge¨ıllustreerd in volgend stukje code. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:v="views.*" layout="vertical"> <mx:Script> <mx:ApplicationControlBar dock="true" horizontalAlign="center"> <mx:LinkBar dataProvider="content" /> <mx:ViewStack id="content" resizeToContent="true" paddingBottom="15" paddingLeft="15" paddingRight="15" paddingTop="15">
Zoals we kunnen zien, wordt voor alle Flex-componenten de naamruimte “http://www.adobe.com/2006/mxml” gebruikt, gespecificeerd door de prefix mx. De ZoekWindow - en KaartWindow -objecten worden via een ViewStack aan de applicatie toegevoegd, het nut van de ViewStack wordt later uitgelegd. We declareren ook een naamruimte met als prefix v voor de klassen die gemaakt zijn in de views-map door “xmlns:v=”views.*”’ aan de Application-tag toe te voegen. Het Script-blok, aangegeven door de mx:Script-tag, wordt gebruikt om ActionScript-code te laten uitvoeren. In dit blok wordt het XML-object vonnissen gedeclareerd dat de vonnissen zal bevatten die getoond worden in de tabel. Voor de declaratie staat de metadata-tag [Bindable], hiermee wordt aangeduid de variabele “bindable” is. Dit betekent dat het object bijvoorbeeld kan gekoppeld worden aan een Datagrid (een DataGrid is een soort van tabel) waarmee de data van het
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
84
object kan weergeven worden. Wanneer de inhoud van dit object echter verandert, zal de weergave in het DataGrid automatisch wijzigen. In de ZoekWindow -tag wordt de variabele vonnissen doorgegeven aan het ZoekWindow -object die ook een variabele vonnissen bevat. In de code “vonnissen={vonnissen}” slaat de tweede ’vonnissen’ op de variable vonnissen in het ZoekWindow -object en dit kan vergeleken worden met een parameter gebruikt bij een constructor zoals in Java. Via de accolades wordt in ActionScript naar variabelen gerefereerd. In ActionScript wordt er namelijk met referenties naar variabelen gewerkt, zodat er niet nodeloos gekopieerd wordt. De ZoekWindow - en KaartWindow -objecten zijn zoals in de code te zien is, toegevoegd aan een ViewStack -object dat op zijn beurt aan het Application-object is toegevoegd. In het LinkBar -object wordt dit ViewStack -object via zijn id ’content’ ingesteld als dataprovider. Via deze constructie wordt er op het scherm altijd ´e´en object getoond en kan men via de LinkBar bovenaan een ander object laten zien. Door het LinkBar -object toe te voegen aan een ApplicationControlBar -object wordt het LinkBar -object als het ware bovenaan de applicatie geplakt. Wanneer de resulterende SWF in een browser uitgevoerd wordt, is het resultaat zoals in figuur 8.2 te zien. De klikbare namen
Figuur 8.2: De LinkBar
die bovenaan in de Linkbar te zien zijn, worden bepaald door label -eigenschap meegegeven in de tag van de desbetreffende object zoals te zien in bovenstaande code. Op de figuur is het ZoekWindow -object geselecteerd dat voorlopig enkel nog maar een stukje tekst bevat. 8.2.0.3
Toegang tot de webservice
Het object dat de toegang tot de webservice voorziet, wordt via een mx:WebService-tag aan “criminaliteit.mxml” toegevoegd. In deze tag moet de WSDL van de webservice meegegeven worden, wat ge¨ıllustreerd wordt in de volgende code. <mx:WebService id="ws" showBusyCursor="true" wsdl="@Resource(key=’WSDL’, bundle=’criminaliteitprop’)" fault="faultHandler(event)"> <mx:operation name="zoekVonnissen" resultFormat="e4x"
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
85
result="wsVonnisHandler(event)"> <mx:request> <misdrijf> <mx:operation name="zoekPlaatsen" <mx:request/>
resultFormat="e4x" result="wsPlaatsHandler(event)">
Via de code “@Resource(key=’WSDL’, bundle=’criminaliteitprop’)” wordt verwezen naar een bestand “criminaliteitprop.properties” dat naam-waarde paren bevat voor constanten. Wanneer men een methode van de webservice wil gebruiken, moet er een operation-tag geplaatst worden in de WebService-tag. In het codevoorbeeld wordt onder meer de operatie zoekVonnissen aan het WebService-object toegevoegd waarbij een parameter misdrijf moet meegegeven worden. Door het attribuut resultFormat van de operation-tag de waarde “e4x” mee te geven kan het SOAP-antwoord als een XML-object benaderd worden. Wanneer de applicatie het SOAP-antwoord ontvangt, wordt de functie wsPlaatsHandler(event) uitgevoerd met als parameter het gegenereerde Event-object. Door het attribuut showBusyCursor van de Webservice-tag gelijk te stellen aan true, krijgt de gebruiker een klokje te zien als cursor tijdens het wachten op een SOAP-antwoord. Via het Fault-attribuut in dezelfde tag wordt bepaald welke functie moet uitgevoerd worden, wanneer er een fout optreedt. Dit WebService-object wordt meegegeven aan ieder object in de views-map die de webservice wil aanroepen op dezelfde manier waarop de variabele vonnissen doorgegeven werd aan het ZoekWindow -object. Deze webservice is dus ook meegegeven aan het ZoekWindow -object en heeft daar de naam ws gekregen. Via het ZoekWindow -object is het mogelijk zijn om vonnissen op te zoeken en te laten weergeven. Als voorbeeld wordt in ZoekWindow een knop geplaatst, die bij een muisklik de methode zoekVonnissen van de webservice laat uitvoeren. Dit wordt ge¨ıllustreerd in volgende code. <mx:Button label="vraag vonnissen op" click="ws.zoekVonnissen.send(’geldmisdrijven’)"/>
Wanneer op deze knop wordt gedrukt , wordt de webservice (hier ws genaamd) gecontacteerd en het resultaat van de methode zoekVonnissen gevraagd. Er wordt ook een parameter meegegeven, die door Flex automatisch gezien wordt als de misdrijf -parameter zoals aangegeven in de desbetreffende operation-tag van de webservice. Wanneer de webservice nu data terugstuurt, moet dit natuurlijk ook opgevangen worden. Deze resultaatsafhandeling gebeurt door de functie die we hiervoor hebben opgegeven in de operation-tag. Deze functie wordt geplaatst in het scriptblok van het hoofdbestand en ziet er als volgt uit: import mx.rpc.events.ResultEvent; private function wsVonnisHandler(event:ResultEvent):void
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
86
{ vonnissen=XML(event.result); }
Met deze functie wordt als parameter een Event-object meegegeven en aangezien het een webservice betreft, is die van het type ResultEvent. Deze parameter wordt, zoals gebruikelijk in ActionScript, eerst aangegeven door de naam van de variabele gevolgd door een dubbelpunt en dan het type van de variabele. Dit event-object bevat het resultaat van de uitgevoerde methode op de webservice en kan opgevraagd worden via result. De variabele vonnissen wordt gelijkgesteld aan dit resultaat, al moet het resultaat eerst gecast worden naar een XML-object. Aangezien vonnissen voorlopig nog nergens aan gekoppeld is, wordt het resultaat ook niet weergegeven. Daarom gaan we deze variabele koppelen aan een DataGrid waarmee onze data duidelijk kan worden weergegeven in het ZoekWindow. Om de code echter overzichtelijk te houden, wordt hiervoor een aparte component gemaakt, genaamd VonnisDatagrid. De hierop volgende code is de code van Zoekwindow. <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" xmlns:comp="components.*"> <mx:Script > <mx:Button label="vraag vonnissen op " click="ws.zoekVonnissen.send(geldmisdrijven)"/>
Aangezien deze component geplaatst zal worden in de map components wordt hiervoor de naamruimte comp gebruikt. ZoekWindow is gebaseerd op de VBox -klasse en hierdoor worden de componenten in ZoekWindow van boven naar beneden geplaatst. Bij de variabele vonnissen valt op te merken dat bij een inhoudsverandering van de vonnissen in het hoofdbestand, dit ook automatisch het geval is voor vonnissen in ZoekWindow. Dit omdat ze beide Bindable zijn gedeclareerd en zoals eerder gezien aan elkaar zijn toegewezen in de ZoekWindow -tag van het hoofdbestand. Voor VonnisDataGrid besproken wordt, gaan we eerst eens het bericht dat de applicatie teruggestuurd krijgt van de webservice van naderbij bekijken. In de volgende code is de inhoud van de variabele vonnissen te zien na het raadplegen van de webserver.
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
87
1530-01-16 1 <misdrijf>diefstallen (beurzen snijden, geld) <misdrijfplaats> Brugge: Boterhuis en botermarkt onbepaald <straf>schavot, geseling en verbanning (10 jaar uit Vlaanderen ) ghegheesselt te zine met roeden up zijn naecte lijf ...
Dit resultaat bevat ´e´en Vonnis-object. Door Flex is de omhullende SOAP-enveloppe- en SOAPBody-tag al verwijderd. In de desbetreffende operation-tag hebben we aangegeven dat we het antwoord van de webserver willen benaderen op de E4X -wijze. Hierdoor is het mogelijk om de straf van het eerste vonnis uit het resultaat op te vragen via volgende code vonnissen.Vonnis[0].straf
Bij het adresseren van een element uit een XML-object, moet de omhullende tag nooit opgegeven worden (in dit geval zoekVonnissenResponse). Deze code blijkt echter niet te werken, de reden hiervoor is te zoeken bij de standaardnaamruimte “http://ws.web.criminaliteit.iii.hogent.be” waartoe de elementen van ons resultaat behoren. Om de elementen te kunnen opvragen, moeten we deze naamruimte ook aangeven in het desbetreffende bestand. Het toevoegen van de hierop volgende code in het scriptblok lost ons probleem op. private namespace nscrim = "http://ws.web.criminaliteit.iii.hogent.be"; use namespace nscrim;
8.2.0.4
De koppeling van het resultaat aan een DataGrid -object
De component die instaat voor de weergave van de variabele vonnissen is zoals eerder aangegeven gebaseerd op de DataGrid -klasse en wordt onder de naam VonnisDataGrid in de map components geplaatst. De code voor deze component is hieronder weergegeven. <mx:DataGrid xmlns... dataProvider="{vonnissen.Vonnis}" > <mx:Script> <mx:columns> <mx:DataGridColumn dataField="straf" headerText="straf" wordWrap="true"/>
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
88
<mx:DataGridColumn dataField="toelichtingstraf" headerText="toelichting straf" .../> <mx:DataGridColumn dataField="datum" headerText="datum"/> <mx:DataGridColumn dataField="misdrijfplaats.naam" headerText="plaats misdrijf" />
Het dataProvider -attribuut voor het Datagrid wordt ingesteld op “vonnissen.Vonis” wat eigenlijk een XML-tabel van Vonnissen is (dus zonder de omhullende zoekVonnissenResponsetag). Aan het DataGrid worden verschillende kolommen toegevoegd die verwijzen naar een veld in de DataProvider. Deze constanten zijn in dit voorbeeld ter verduidelijking rechtstreeks ingevoerd, maar in de uiteindelijke versie zal gebruik gemaakt worden van een Propertiesbestand. Het VonnisDataGrid zal dus leeg zijn tot wanneer de webservice een antwoord heeft teruggestuurd aangezien de variable vonnissen hier ook gekoppeld is aan vonnissen uit ZoekWindow. Wanneer we deze code uitvoeren en de webservice contacteren, blijkt er echter geen enkel vonnis in het VonnisDatagrid -object getoond te worden. De reden hiervoor is terug te zoeken bij het gebruik van de standaardnaamruimte waartoe de elementen van het vonnissen-object nog steeds behoren. Bij het gebruik van een DataGrid moet de opdracht tot het gebruik van een naamruimte iedere keer gegeven worden voor er een item uit de lijst wordt afgedrukt op het scherm. Om dit te kunnen verwezenlijken gaan we een labelFunctionGrid functie aanmaken die bij het behandelen van ieder item in het DataGrid opgeroepen wordt. Deze functie kunnen we laten toepassen door in iedere kolom een functie voor het attribuut labelFunction op te geven zoals in onderstaand voorbeeld. <mx:DataGridColumn dataField="straf" headerText="straf" labelFunction="labelFunctionGrid" wordWrap="true"/>
De functie die we toevoegen aan het scriptblok ziet er als volgt uit: private function labelFunctionGrid(obj:Object, datagridcolumn:DataGridColumn):String{ use namespace crim; var q:QName = new QName(crim, datagridcolumn.dataField); var string:String = obj[q]; return string; }
Door het Flex-framework wordt automatisch als parameter het object dat moet weergegeven worden en de naam van de datakolom doorgegeven. Vervolgens wordt een qualified name voor het object geconstrueerd via de naamruimte en het dataveld van kolom (die gelijk is aan de naam van het item in het XML-object). Via deze qualified name kan de string die moet weergegeven worden uit het object gehaald worden. Voor het dataveld datum wordt een aangepaste versie van de labelFunctionGrid -functie voorzien zodat de datum correct weergegeven wordt via een DateFormatter. In deze versie van de functie wordt ook het plaats misdrijf -veld behandeld aangezien dit veld dieper genesteld zit in de XML-structuur (via het
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
89
misdrijfplaats-object) en dus een aangepaste benadering vereist. Wanneer de applicatie na deze toevoegingen de resultaten van de webservice terugkrijgt, wordt dit wel correct in het VonnisDataGrid -object weergegeven zoals te zien in figuur 8.3.
Figuur 8.3: Het VonnisDataGrid -object
8.2.1
Het toevoegen van een zelfgedefinieerde Event-klasse
Wanneer de gebruiker op een vonnis in het VonnisDataGrid klikt, wordt het vonnis automatisch geselecteerd zoals te zien is bij het tweede vonnis in figuur 8.3. Na deze selectie is het mogelijk om de knoppen die naast de tabel staan aan te klikken om verdere informatie over het geselecteerde vonnis op te vragen. Dit wordt in de applicatie gerealiseerd door de bestaande Event-klasse uit te breiden met een klasse ItemSelectedEvent. Deze klasse wordt ge¨ımplementeerd als een ActionScript-component en heeft hierdoor ook de extensie “.as” in plaats van “.mxml” en wordt in de map events geplaatst. De code voor deze klasse wordt in de volgende code getoond. package events { import flash.events.Event; public class ItemSelectedEvent extends Event { public var vonnisid:int; public function ItemSelectedEvent(vonnisid:int, type:String) { super(type); this.vonnisid = vonnisid; } override public function clone():Event { return new ItemSelectedEvent(vonnisid,type); } } }
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
90
Deze klasse wordt geheel in ActionScript gecodeerd en breidt de bestaande klasse flash.events.Event uit. Voor een Event moet verplicht een type aangegeven worden die meegegeven wordt via de constructor aan de bovenliggende klasse. Bij het uitbreiden van de Event-klasse moet de clone-functie ook verplicht overschreven worden. Met het ItemSelectedEvent-object wordt het id van het vonnis (vonnisid) meegeven dat in het VonnisDataGrid geselecteerd is. Er moet een ItemSelectedEvent-object gegenereerd worden wanneer er op een vonnis uit het VonnisDataGrid geklikt wordt. Om dit te verwezenlijken wordt de code uit VonnisDataGrid.mxml als volgt aangepast. <mx:DataGrid ... dataProvider="{vonnissen.Vonnis}" itemClick="itemClicked(event)"> <mx:Metadata> [Event(name="itemSelected", type="events.ItemSelectedEvent")] <mx:Script> <mx:columns>...
In de DataGrid -tag wordt een functie itemClicked opgegeven die wordt uitgevoerd bij het het selecteren van een vonnis uit de lijst. Als parameter wordt aan deze functie een ListEvent-object meegegeven die onder meer de data van het geselecteerde vonnis bevat via het attribuut target die het Datagrid voorstelt. In deze functie wordt er een nieuw ItemSelectedEvent-object aangemaakt met als parameters het vonnisid en het type. Vervolgens wordt dit ItemSelectedEvent-object gedispatched en dus verspreid naar de component die deze gebeurtenis opvangt. In ons geval moet het ZoekWindow -object waar dit VonnisDataGrid -object gedeclareerd staat, de gebeurtenis opvangen. Om dit mogelijk te maken wordt in VonnisDataGrid een Metadata-tag toegevoegd die de naam en het type van de gebeurtenis definieert. De aanpassingen die moeten gebeuren aan de code van ZoekWindow zien er tenslotte als volgt uit: <mx:VBox ...> <mx:Script >
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
91
<mx:HBox width="100%"> <mx:Button id="btnarchief" label="Bekijk Archiefbronnen" enabled="false" click="clickBtnArchief()"/>
Naast het VonnisDataGrid is een knop geplaatst waarmee informatie over de desbetreffende archiefbronnen kan opgevraagd worden. Het VonnisDataGrid en de knop btnarchief worden samen in een HBox geplaatst zodat ze naast elkaar komen te staan. In de VonnisDataGrid -tag wordt een functie dataGridItemSelected opgegeven die uitgevoerd wordt wanneer een item of vonnis in de lijst geselecteerd wordt. Dit wordt aangegeven door het attribuut itemSelected dat we opgegeven hebben als type in de MetaData-tag van VonnisDataGrid. In de functie dataGridItemSelected wordt de knop btnarchief klikbaar gemaakt en wordt de variabele vonnisid ingesteld via het vonnisid -attribuut van het ItemSelectedEvent-object. Zo is het mogelijk om gedetailleerde informatie over het vonnis op te vragen aangezien we de knoppen hiervoor klikbaar kunnen maken als het nodig is en beschikken over het id van het geselecteerde vonnis in de lijst.
8.2.2
Gedetailleerde informatie tonen via een popupscherm
De gebruiker kan meer informatie over een vonnis vragen door te klikken op de knoppen naast het VonnisDataGrid en zo een popupscherm te openen met de desbetreffende informatie. Er worden dergelijke schermen voorzien voor de archiefbronnen, de plaatsen, de verdere gedetailleerde informatie over het vonnis... Deze schermen worden niet modaal gemaakt zodat er ondertussen kan gekeken worden in de vonnissenlijst. Dit heeft ook als voordeel dat verschillende popupschermen tegelijk kunnen geopend zijn en de inhoud van deze schermen kan vergeleken worden. In deze sectie wordt als voorbeeld een popupscherm voor het weergeven van de archiefbronnen gemaakt. De webservice beschikt over een methode getArchiefBronnen(vonnisid) die een tabel van archiefbronnen (er kunnen immers meerdere archiefbronnen per vonnis zijn) teruggeeft. Een voorbeeld van een SOAP-antwoord met 1 archiefbron is hieronder weergegeven.
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
92
<archiefadres>ARA, Rk. 13783, 12 aug. 1518 - 12 aug. 1519 (Jacob van Halewin, heer van Maldeghem en Uitkerke) <archiefplaatsnaam>Brugge 151, 105-106 pay´ e de justice, verluydboek
Dit resultaat wordt opgeslagen in het XML-object archiefbronnen. Het popupscherm voor het weergeven van de archiefbronnen wordt gecodeerd in de klasse PopupArchief. Om een popupscherm te tonen wordt volgende functie aan ZoekWindow toegevoegd die uitgevoerd wordt wanneer de applicatie het SOAP-antwoord met de archiefbron(nen) ontvangt. public function showPopupArchief():void{ var popup:IFlexDisplayObject = PopUpManager.createPopUp (this, PopupArchief, false); PopupArchief(popup).setArchiefBronnen(archiefbronnen); PopUpManager.centerPopUp(popup ); PopupArchief(popup).title="De archiefbronnen"; }
Er wordt gebruik gemaakt van de statische klasse createPopUp van PopUpManager waarbij als eerste parameter het ouderobject (in ons geval ZoekWindow) meegegeven wordt. De tweede parameter is de klasse van het popupscherm en als laatste parameter wordt via een Booleanwaarde aangegeven of het scherm al dan niet modaal moet zijn. De methode geeft de variabele popup met als type IFlexDisplayObject terug. Dit is een interface voor het weergeven van visuele elementen. Via de variabele popup kan men vervolgens de variabele archiefbronnen doorgeven aan PopupArchief via de methode setArchiefBronnen(...) die daarin te vinden is. Via een andere statische methode van PopUpManager wordt het popupscherm in het midden van het scherm weergegeven. Voor de klasse PopupArchief wordt als basisklasse TitleWindow genomen waarmee een venster kan aangemaakt worden. Het bestand voor deze klasse wordt in de map views geplaatst en de code wordt in het hierop volgende stuk tekst getoond. <mx:TitleWindow ... showCloseButton="true" borderAlpha="1.0" creationComplete="addEventListener(CloseEvent.CLOSE,close);"> <mx:Script>
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
93
} public function setArchiefBronnen(archiefbronnen:XML):void{ this.archiefbronnen=archiefbronnen; } ]]> <mx:ControlBar> <mx:LinkBar dataProvider="{archiefbronnenstack}" /> <mx:ViewStack id="archiefbronnenstack" width="100%" height="80%"> <mx:Repeater id="archiefbronrepeater" dataProvider="{archiefbronnen.ArchiefBron}"> <mx:Canvas label="bron {archiefbronrepeater.currentIndex+1}" width="100%" height="100%"> <mx:Label x="10" y="0" text="ArchiefAdres:" fontWeight="bold" /> <mx:Text xy... text="{archiefbronrepeater.currentItem.archiefadres}"/> <mx:Label x="10" y="25" text="Bron:" fontWeight="bold"/> <mx:Text x="100" y="25" text="{archiefbronrepeater.currentItem.bron}"/> <mx:Label x="10" y="50" text="Rubriek:" fontWeight="bold"/> <mx:Text x="100" y="50" text="{archiefbronrepeater.currentItem.rubriek}"/> <mx:Label x="10" y="75" text="Folio:" fontWeight="bold"/> <mx:Text x="100" y="75" text="{archiefbronrepeater.currentItem.folio}"/> <mx:Label x="10" y="100" text="Archiefplaats:" fontWeight="bold"/> <mx:Text xy...text="{archiefbronrepeater.currentItem.archiefplaatsnaam}"/>
In de TitleWindow -tag wordt aangegeven dat er een CloseButton aan het venster moet toegevoegd worden en wordt er een functie opgegeven die moet uitgevoerd worden wanneer er op deze knop gedrukt wordt. Aangezien het resultaat verschillende archiefbronnen kan bevatten, wordt er terug gebruik gemaakt van een LinkBar gekoppeld aan een ViewStack. In deze ViewStack vindt men nu echter een Repeater. Dit is een Flex-component die verschillende instanties aanmaakt van zijn subcomponenten aan de hand van de gespecificeerde dataProvider. In ons geval is deze subcomponent een Canvas-object waaraan componenten kunnen toegevoegd worden. Aangezien de dataProvider van de Repeater gelijk is aan “archiefbronnen.ArchiefBron” wordt er voor iedere ArchiefBron in de variabele archiefbronnen een Canvas-object aangemaakt. Op ieder Canvas-object wordt de desbetreffende informatie via Label - en Text-componenten getoond. Het resultaat is te zien in figuur 8.4. In de figuur is een venster geopend met informatie over 3 archiefbronnen. Via de Linkbar bovenaan kan de informatie over de gewenste bron getoond worden.
8.2.3
De kaartencomponent
Via de kaartenapplicatie is er een koppeling gemaakt tussen de plaatsen in de databank en punten op een kaart (zie hoofdstuk 5). Het tonen van deze kaarten met de aangeduide punten in de webapplicatie gebeurt in KaartWindow. Deze klasse bevindt zich in de views-map en
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
94
Figuur 8.4: Het popupvenster voor de archiefbronnen
kan geselecteerd worden in de applicatie via de LinkBar bovenaan. De gebruiker kan een kaart selecteren uit de set van de in de databank aanwezige kaarten via een keuzemenu. De webservice bevat een methode die de aanwezige kaarten in de databank teruggeeft via een tabel van Kaart-objecten. Een Kaart-object bevat onder meer de URL waar de kaartafbeelding kan opgehaald worden. Na selectie van een bepaalde kaart door de gebruiker, wordt deze kaart getoond op het scherm en worden de punten die gekoppeld zijn aan een plaats uit de databank opgehaald van de webservice en op de kaart aangeduid. Dit aanduiden gebeurt door het tekenen van een cirkel in een kleur volgens het type van de gekoppelde plaats zoals ook gebeurde in de kaartenapplicatie. Wanneer de gebruiker vervolgens op een dergelijke cirkel klikt, wordt er onderaan de kaart verdere informatie over de desbetreffende plaats getoond. Voor het weergeven van de cirkels op de kaartafbeelding moet er getekend worden op deze afbeelding. Aangezien dit tekenen via ActionScript moet gebeuren, wordt de volledige component in diezelfde taal ontwikkeld. Deze klasse, KaartPlaatsen, maakt dus geen gebruik van de MXML-syntax. De klasse wordt ge¨ıntegreerd in onze applicatie via de KaartWindow -klasse, die te vinden is in de views-map. De belangrijkste code voor KaartWindow is weergegeven in onderstaande code. <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initapp()"> <mx:Script>
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
95
public var kaart:XML; [Bindable] public var plaatscoordinaten:XML; ... private function initapp():void { var kaartplaatsenobj:KaartPlaatsen=new KaartPlaatsen(kaart,plaatscoordinaten)as KaartPlaatsen; var wrapper:UIComponent=new UIComponent() addChild(wrapper); wrapper.addChild(kaartplaatsenobj); ]]> ...
In de VBox -tag wordt een functie opgegeven die wordt uitgevoerd na het cre¨eren van deze component. In deze functie wordt een KaartPlaatsen-object aangemaakt waarbij de variabelen kaart en plaatscoordinaten de data opgehaald van de webservice zullen bevatten. De variabele plaatscoordinaten dient voor het bijhouden van de plaatsen uit de databank die gekoppeld zijn aan de variabele kaart en bevat ook de co¨ordinaten ervan. Om deze ActionScript-component te integreren in onze applicatie wordt gebruik gemaakt van een zogenaamde wrapper. Deze wrapper is in dit geval een object van de UIComponent-klasse, de basisklasse voor alle visuele componenten. Via de methode addChild van het UIComponent-object wordt ons KaartPlaatsen-object tenslotte ge¨ıntegreerd in de applicatie. De klasse KaartPlaatsen wordt gecodeerd in het bestand “KaartPlaatsen.as” en wordt in de map components geplaatst. De basiscode voor deze klasse ziet er als volgt uit: package components{ import flash.display.Sprite; import flash.display.Loader; import flash.text.TextField; import flash.text.TextFieldType; import flash.text.TextFieldAutoSize; import flash.events.MouseEvent; public class KaartPlaatsen extends Sprite { private var kaart:XML; private var plaatscoordinaten:XML; private var textfield:TextField; private var crimns:Namespace = new Namespace("ws.web...be"); default xml namespace = crimns; public function KaartPlaatsen(kaart:XML,plaatscoordinaten:XML){ this.kaart=kaart; this.plaatscoordinaten=plaatscoordinaten; var loader:Loader=new Loader();
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
96
var kaarturl:String=kaart.url; addChild(loader); loader.load(new URLRequest(kaarturl)); this.textfield= new TextField( ); textfield.autoSize = TextFieldAutoSize.LEFT; textfield.y=250; addChild(textfield); } } }
Aangezien dit een zuivere ActionScript-component is, wordt hier geen gebruik gemaakt van de Flex-mx -klassen maar van de Flash-klassen. De KaartPlaatsen-klasse is een uitbreiding van de bestaande Flash-klasse Sprite, die afbeeldingen kan weergeven en ook zelf objecten kan bevatten. De klasse KaartPlaatsen bestaat uit een Loader -object die de kaart weergeeft samen met een tekstveld die informatie over de geselecteerde plaats op de kaart kan tonen. Deze objecten worden via de methode addChild van de Sprite-klasse aan de layout van het KaartPlaatsen-object toegevoegd. Er wordt via de opdracht “default xml namespace” terug een standaardnaamruimte ingesteld, zodat we de XML-objecten van de webservice kunnen inlezen. Via de Loader -klasse kunnen SWF-bestanden of afbeeldingen ingeladen worden en op het scherm getoond worden. Aan het Loader -object wordt de URL die in het kaart-object zit, meegegeven. Voor het tekenen van een cirkel wordt een nieuw Sprite-object aangemaakt die de getekende cirkel bevat. Dit is ge¨ıllustreerd in volgende code: public function createCircle( color:uint, radius:Number ):Sprite { var sprite:Sprite = new Sprite( ); sprite.graphics.beginFill( color ); sprite.graphics.drawCircle( 0, 0, radius ); sprite.graphics.endFill( ); return sprite; }
Voor het opvullen van de cirkel met een bepaalde kleur wordt gebruik gemaakt van de methoden beginFill en endFill. We kunnen deze methode nu gebruiken om de plaatsen aanwezig in de variabele plaatscoordinaten te tekenen op de kaart zoals te zien in onderstaande code. var getal:uint=0; for each ( var plaats:XML in plaatscoordinaten) { var circle:Sprite = createCircle( 0xFF0000, plaats.r); circle.x = plaats.x; circle.y = plaats.y; circle.name=String(getal++); addChild(circle); }
Om de cirkel te tekenen moeten we het Sprite-object, teruggegeven van de functie createCircle die de cirkel voorstelt, toevoegen aan de layout van het KaartPlaatsen-object. Dit gebeurt
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
97
via de methode addChild nadat de locatie van de cirkel is ingesteld. Via het attribuut name van het circle-object zijn we achteraf in staat om de plaats te achterhalen waartoe de cirkel behoort. Wanneer op een dergelijke cirkel met de muis geklikt wordt, moet informatie over de plaats die gekoppeld is aan dat punt getoond worden in het tekstveld onderaan. Hiervoor wordt een EventListener aan iedere cirkel toegevoegd en dit kan door volgende lijn code in de bovenstaande for each-lus toe te voegen. circle.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown );
Door op een cirkel te klikken, activeert de gebruiker dus de functie handleMouseDown. De inhoud van deze functie wordt in volgende code getoond. public function handleMouseDown( event:MouseEvent ):void { var sprite:Sprite=Sprite(event.currentTarget); field.text=plaatscoordinaten.plaats[sprite.name].naam; }
Door het opvragen van de name-attribuut van het Sprite-object, kunnen we de plaats achterhalen en dus ook de naam ervan toekennen aan het tekstveld. Via deze methode zijn we dus in staat om gelijk welk veld van het plaats-object in het tekstveld weer te geven.
8.2.4
Tekenen traject van een misdadiger op een kaart
Voor de webapplicatie is ook een ActionScript-component ontwikkeld die het traject van een bepaalde misdadiger op kaart tekent. Aan de hand van de plaatsen die te vinden zijn in de vonnissen van een bepaalde misdadiger is het mogelijk om een vermoedelijk traject van die misdadiger te bepalen. De component, genaamd KaartTraject, geeft de plaatsen waaruit dit vermoedelijke traject bestaat chronologisch weer door middel van een cirkel met een cijfer in. De eerste plaats krijgt natuurlijk het cijfer “1” en de plaatsen worden zo oplopend genummerd. Zoals in de KaartPlaatsen-component krijgt de gebruiker na het klikken op een cirkel meer informatie over de desbetreffende plaats te zien. Alle plaatsen worden tenslotte verbonden door een lijn. In een eerste versie van de KaartTraject-component wordt voor het weergeven van de cirkels gebruik gemaakt van kleine zelfgemaakte PNG-afbeeldingen. Voor de gebruikte cijfers is er een afbeelding beschikbaar die het desbetreffende cijfer in een cirkel afbeeldt. In de onderstaande code wordt getoond hoe de afbeeldingen in het KaartTraject-object worden weergegeven. private var piceen:Bitmap; private var pictwee:Bitmap; private var picArray:Array; [Embed(source="een.png")] private var een:Class; [Embed(source="twee.png")] private var twee:Class; piceen = new een;
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
98
pictwee = new twee; picArray.push(piceen); picArray.push(pictwee); var picHolder:Sprite = new Sprite(); picHolder.addChild(picArray[0]); picHolder.x = punt.10; picHolder.y = punt.10; addChild(picHolder); var picHolder2:Sprite = new Sprite(); picHolder2.addChild(picArray[1]); picHolder.x = punt.10; picHolder.y = punt.50; addChild(picHolder2);
Deze code plaatst de cirkels met het bijbehorende cijfer “1” en “2” onder elkaar. Via de klasse Bitmap en de tag Embed, waar de plaats van de afbeelding aangegeven wordt, worden de afbeeldingen ge¨ınitialiseerd. Vervolgens worden de afbeeldingen in een tabel gestopt. Voor het tekenen van de cirkels wordt gebruik gemaakt van een Sprite-object, waar de afbeelding aan toegevoegd wordt. Aangezien er wordt gewerkt met vooraf gemaakte afbeeldingen worden deze aan het SWF-bestand zelf toegevoegd, waardoor dit bestand groter wordt en de cli¨entapplicatie ook meer geheugen nodig heeft. Daarom is er een tweede versie van KaartTraject gemaakt die dit probleem oplost. In de tweede versie van de component wordt niet meer gewerkt met vooraf aangemaakte afbeeldingen. De cirkels met een cijfer erin worden nu bij het uitvoeren van het programma bij de cli¨ent op het scherm getekend via de Flash API. Er is ook een timer aan de component toegevoegd zodat het traject ‘in realtime’ stap voor stap op het scherm wordt getekend. De code van de functie die de cirkels met een cijfer in construeert is hieronder weergegeven. public function createTextCircle( color:uint, radius:Number,nr:Number ):Sprite { var sprite:Sprite = new Sprite( ); sprite.graphics.lineStyle(2); sprite.graphics.beginFill( color ); sprite.graphics.drawCircle( 0, 0, radius ); sprite.graphics.endFill( ); var text:TextField=new TextField(); text.text=String(nr); var formatter:TextFormat=new TextFormat; formatter.bold = true; formatter.color = 0xFFFFFF; formatter.size=29; formatter.font="Tahoma"; text.setTextFormat(formatter); text.x=-11; //vanaf een hoek van de Sprite gerekend text.y=-21; sprite.addChild(text); return sprite; }
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
99
In figuur 8.5 is een afdruk te zien van een traject weergegeven door de tweede versie van KaartTraject.
Figuur 8.5: Het traject van een misdadiger
Er is echter beslist om de component KaartTraject niet te integreren in de webapplicatie. Het construeren van een traject voor een misdadiger is maar mogelijk in enkele particuliere gevallen en het is het is eerder de bedoeling dat de webapplicatie meer algemene zaken behandelt.
8.3
Realisatie applicatie
In dit hoofdstuk is aangegeven hoe de webapplicatie met Flex wordt geconstrueerd. De voorbeelden die in dit hoofdstuk gegeven zijn, bepalen nog niet hoe de webapplicatie er precies zal uitzien, maar zijn enkel behandeld om de algemene technieken die gebruikt worden aan te tonen. Deze applicatie is op het moment van schrijven immers nog niet geheel afgewerkt en zal verder afgewerkt in de periode tussen het indienen van deze scriptie en de verdediging ervan. De afgewerkte webapplicatie zal de vonnissen ook in een DataGrid tonen, de velden die hierin zullen worden getoond zijn aangegeven door de heer Dupont. Om te zoeken naar vonnissen zal
Hoofdstuk 8. De webapplicatie ontwikkeld met Flex 2
100
het mogelijk zijn om bijvoorbeeld te zoeken via een persoonsnaam van dader of slachtoffer, het soort misdrijf of straf, de plaats van het misdrijf... Verder worden er nog aanvullende popupschermen voorzien, zodat meer gedetailleerde informatie kan opgevraagd worden. Via de kaartencompontent worden plaatsen op de kaart weergegeven en wanneer de gebruiker er vervolgens op klikt, wordt informatie erover getoond.
HOOFDSTUK
9
Besluit
Bij aanvang van de thesis was er over het onderwerp niet meteen duidelijkheid, het was dan ook een grote sprong in het duistere. Ik had er echter het volle vertrouwen in dat de uiteindelijke doelstellingen van het project een mooie uitdaging zouden vormen. Eenmaal het project enige vorm kreeg, werden de doelstellingen duidelijker en werden ook te ontwikkelen functionaliteiten aan het project toegevoegd zodat de informatie in databank ten volle zou kunnen benut worden. De originele Access-databank, die werd gebruikt door de heer Dupont en een niet onaardig aantal records bevatte, had een te slechte structuur om er effici¨ent mee te kunnen werken. Hierdoor was het toevoegen en wijzigen van data geen sinecure en waren gerichte opzoekingen moeilijk, dikwijls zelfs onmogelijk, uit te voeren. Door middel van de conversieapplicatie is de Access-databank geconverteerd naar een PostgreSql-databank met een fel verbeterde structuur. Hierdoor kan veel meer informatie uit de databank gehaald worden. De administratieapplicatie was echter een essentieel onderdeel om de databank verder te optimaliseren, maar is jammer genoeg niet in de relatief korte tijdspanne van deze thesis kunnen ontwikkeld worden. Via de webapplicatie wordt het mogelijk om de databank via het internet te benaderen. Zoals in de deze thesis duidelijk naar voor kwam, gebeurt dit op een visueel aantrekkelijke wijze door het Flex 2-framework. Door de integratie van de kaartencomponent in de webapplicatie worden de plaatsen op een kaart weergegeven. Het effectieve ontplooien van de webapplicatie op een webserver is hier nog niet aan de orde, aangezien de heer Dupont nog niet zeker weet of hij de website wil publiceren. Hij wil in eerste instantie namelijk eerst de publicatie van zijn doctoraat afwachten. 101
Hoofdstuk 9. Besluit
102
Deze thesis was voor mij persoonlijk een erg leerrijke ervaring die me in het latere bedrijfsleven zeker goed van pas zal komen. Door dit project ben ik met erg veel nieuwe technologie¨en in contact gekomen en kreeg ik de kans om ze ook in de praktijk om te zetten. Deze thesis is hopelijk niet het eindpunt van dit project aangezien de ontwikkeling van een administratieapplicatie onontbeerlijk is voor het bereiken van een goed functionerend geheel. Ook aan de webapplicatie kunnen zeker nog dingen verbeterd worden, denken we maar aan een betere voorstelling van straten en gebieden op een kaart. In deze thesis is met andere woorden een basis gelegd voor een volgend thesisproject dat zeker nog enkele interessante uitdagingen bevat.
BIJLAGE
A
Een record uit de originele Access-databank
Op de volgende pagina is ter illustratie een record uit de Access-databank via formulierweergave weergegeven.
103
Bijlage A. Een record uit de originele Access-databank
Figuur A.1: Een record uit de originele Access-databank
104
BIJLAGE
B
Een voorlopige versie van het databankschema
In figuur B.1 wordt een versie van het databankschema getoond waarbij nog gebruik gemaakt is van de tabellen Straf en Misdrijf in plaats van de tabel Vonnis.
105
Bijlage B. Een voorlopige versie van het databankschema
Figuur B.1: Databankschema versie 1
106
BIJLAGE
C
De DDL van de PostgreSql-databank
Via deze DDL worden de tabellen besproken in hoofdstuk 3.4 en de tabellen voor de koppeling tussen de plaatsen en de kaarten (zie hoofdstuk 5 geplaatst in de databank). Na het inlezen worden nog enkele ‘INSERT’-opdrachten uitgevoerd om de codes met de bijbehorende namen aan de databank toe te voegen. drop table medeplichtigennaam CASCADE; drop table vonnis CASCADE; drop table .... drop SEQUENCE seq_kaart_id; drop SEQUENCE ... create SEQUENCE seq_kaart_id; create SEQUENCE ... create table kaart ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_kaart_id’), naam text NOT NULL, dir text NOT NULL, breedte text NOT NULL, hoogte text NOT NULL, periode text, PRIMARY KEY(id,naam) ); create table type ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_type_id’), naam text NOT NULL, PRIMARY KEY(id,naam) 107
Bijlage C. De DDL van de PostgreSql-databank ); create table plaats ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_plaats_id’), naam text NOT NULL, nr text, actuelenaam text, typeid integer REFERENCES type(id) NOT NULL, PRIMARY KEY(id,naam,typeid) ); create table plaatsplaats ( plaatsid integer NOT NULL REFERENCES plaats(id) on delete cascade , ouderplaatsid integer NOT NULL REFERENCES plaats(id) on delete cascade, PRIMARY KEY(plaatsid,ouderplaatsid) ); create table plaatskaart ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_plaatskaart_id’), plaatsid integer NOT NULL REFERENCES plaats(id) on delete cascade , kaartid integer NOT NULL REFERENCES kaart(id) on delete cascade, coordinaatx integer NOT NULL, coordinaaty integer NOT NULL, PRIMARY KEY(plaatsid,kaartid,id) ); create table soortstraf ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_soortstraf_id’), code text NOT NULL, naam text , PRIMARY KEY(id) ); create table voorzienestraf ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_voorzienestraf_id’), code text NOT NULL, naam text , PRIMARY KEY(id) ); create table procedure ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_procedure_id’), code text NOT NULL, naam text , PRIMARY KEY(id) ); create table vzcode ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_vz_id’), naam text, code text NOT NULL, PRIMARY KEY(id) ); create table bron (
108
Bijlage C. De DDL van de PostgreSql-databank id integer NOT NULL UNIQUE DEFAULT nextval(’seq_bron_id’), naam text , code text NOT NULL, archiefplaatsid integer REFERENCES plaats(id), PRIMARY KEY(id) ); create table rubriek ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_rubriek_id’), naam text , code text NOT NULL, bronid integer REFERENCES bron(id), PRIMARY KEY(id) ); create table soortmisdrijf ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_soortmisdrijf_id’), naam text , code text NOT NULL, PRIMARY KEY(id) ); create table beroep ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_beroep_id’), naam text NOT NULL, PRIMARY KEY(id) ); create table persoon ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_persoon_id’), plaatsid integer REFERENCES plaats(id), geboorteplaats integer REFERENCES plaats(id), opmgeboorteplaats text, voornaam text, naam text, bronvoornaam text, vormvoornaam text, bronnaam text, vormechtgenoot text, geslacht text, leeftijd text, titel text, herkomst text, isvreemdeling boolean, ispoorter boolean, verwantschap text, prosopografie text, isgenaamd boolean, PRIMARY KEY(id) ); create table beroeppersoon (
109
Bijlage C. De DDL van de PostgreSql-databank beroepid integer NOT NULL REFERENCES beroep(id), persoonid integer NOT NULL REFERENCES persoon(id), opmerking text, PRIMARY KEY(beroepid,persoonid) ); create table verbanning ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_verbanning_id’), datumverbanning date, datumterugkeer date, redenterugkeer text, bronterugkeer text, duur text, PRIMARY KEY(id) ); create table vonnis ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_vonnis_id’), strafplaatsid integer REFERENCES plaats(id), opmstrafplaats text, procedureid integer REFERENCES procedure(id), beschrijving text, verbanningid integer REFERENCES verbanning(id), betaaldeboete text, boetebedragstad text, totaleboetesom text, bedraguitgavenbeul text, uitgavenbeul text, toelichtingstraf text, iscompositienaveroordeling boolean, isklachtenboete boolean, aantalboetes text, bedevaart text, daderid integer REFERENCES persoon(id), misdrijfplaatsid integer REFERENCES plaats(id), opmmisdrijfplaats text, slachtofferid integer REFERENCES persoon(id), jaar text, datum date, dagvandeweek text, aanklachtinleiding text, aantalslachtoffers text, aantaldaders text, aantalbestraftedaders text, relatieslachtofferdader text, isinstadsrekening boolean, desiderata text, misdrijf text, autonr int,
110
Bijlage C. De DDL van de PostgreSql-databank
111
straf text, datumeindvonnis text, verzachtendeomstandigheden text, voorzienestrafid integer REFERENCES voorzienestraf(id), PRIMARY KEY(id) ); create table vonnisarchief ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_vonnisarchief_id’), rubriekid integer REFERENCES rubriek(id), archiefadres text, folio text, vonnisid integer NOT NULL REFERENCES vonnis(id), PRIMARY KEY(id) ); create table vonnissoortmisdrijf ( vonnisid integer NOT NULL REFERENCES vonnis(id), soortmisdrijfid integer NOT NULL REFERENCES soortmisdrijf(id), PRIMARY KEY(vonnisid,soortmisdrijfid) ); create table aanklacht ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_aanklacht_id’), vonnisid integer NOT NULL REFERENCES vonnis(id), afzetplaatsid integer REFERENCES plaats(id), opmafzetplaats text, beschrijving text, PRIMARY KEY(vonnisid,id) ); create table aanklachtslachtoffer ( aanklachtid integer NOT NULL REFERENCES aanklacht(id), slachtofferid integer NOT NULL REFERENCES persoon(id), PRIMARY KEY(aanklachtid,slachtofferid) ); create table vonnissoortstraf ( vonnisid integer NOT NULL REFERENCES vonnis(id), soortstrafid integer NOT NULL REFERENCES soortstraf(id), beschrijving text, duur integer, PRIMARY KEY(vonnisid,soortstrafid) ); create table vonnisvzcode( vonnisid integer NOT NULL REFERENCES vonnis(id), vzcodeid integer NOT NULL REFERENCES vzcode(id), PRIMARY KEY(vonnisid,vzcodeid) ); create table medeplichtigennaam ( id integer NOT NULL UNIQUE DEFAULT nextval(’seq_medeplichtigennaam_id’), medeplichtige text NOT NULL,
Bijlage C. De DDL van de PostgreSql-databank
112
vonnisid integer NOT NULL REFERENCES vonnis(id), opmerking text, PRIMARY KEY(id,vonnisid) ); insert into soortstraf(code,naam) values(’A’,’Ambtsverlies’); insert into soortstraf(code,naam) values(’B’,’Expulsiestraf’); insert into soortstraf(code,naam) values(’C’,’Vermogensstraf’); ... insert into vzcode(code,naam) values(’\$’,’Codes nog toe te kennen’); insert into vzcode(code,naam) values(’B’,’(Vrijwillige) ballingschap van dader ("se tient ... insert into voorzienestraf(code,naam) values(’100ban’,’100 jaar verbanning uit Vlaanderen insert into voorzienestraf(code,naam) values(’100hart’,’100 jaar verbanning uit Vlaanderen ... insert into procedure(code,naam) values(’EV’,’Extra-ordinair proces voor de vierschaar’); insert into procedure(code,naam) values(’K’,’Proces voor de kamer’); ... insert into bron(code,naam) values(’BIC’,’Bouc van den informatien criminele, Brugge’); insert into bron(code,naam) values(’BK’,’Anonieme Brugse kroniek 1477-1491’); ... insert into type(naam,id) values(’onbepaald’,0); insert into type(naam,id) values(’Gebouw’,1); insert into type(naam,id) values(’Straat’,2); insert into type(naam,id) values(’Wijk/Parochie’,3); insert into type(naam,id) values(’Dorp/Stad’,4); insert into type(naam,id) values(’Kasselrij’,5); insert into type(naam,id) values(’Vorstendom’,6);
BIJLAGE
D
De databankstructuur van de PostgreSql-databank
Op de volgende pagina in figuur D.1 is het uiteindelijke databankschema weergegeven, ook de tabellen die instaan voor de koppeling tussen de plaatsen en de kaarten zijn hieraan toegevoegd.
113
Bijlage D. De databankstructuur van de PostgreSql-databank
Figuur D.1: De volledige databankstructuur
114
BIJLAGE
E
De webservice - aanvullingen
In de sectie 7.3.1 werden enkel korte stukjes uit de code uit de WSDL en WSDD van de desbetreffende webservice overgenomen. De volledige inhoud van de bestanden is hieronder echter weergegeven.
WSDL-bericht <wsdl:definitions targetNamespace="http://ws.web.criminaliteit.iii.hogent.be" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://ws.web.criminaliteit.iii.hogent.be" xmlns:intf="http://ws.web.criminaliteit.iii.hogent.be" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <schema elementFormDefault="qualified" targetNamespace="http://ws.web.criminaliteit.iii.hogent.be" xmlns="http://www.w3.org/2001/XMLSchema"> <element name="zoekPlaatsen"> <sequence> <element name="naam" type="xsd:string"/> <element name="nr" type="xsd:string"/> <element name="actuelenaam" type="xsd:string"/> 115
Bijlage E. De webservice - aanvullingen <element name="type" type="xsd:string"/> <element name="typeselected" type="xsd:boolean"/> <element name="zoekPlaatsenResponse"> <sequence> <element maxOccurs="unbounded" name="zoekPlaatsenReturn" type="impl:Plaats"/> <sequence> <element name="actueleNaam" nillable="true" type="xsd:string"/> <element name="naam" nillable="true" type="xsd:string"/> <element name="nr" nillable="true" type="xsd:string"/> <element name="type" nillable="true" type="xsd:string"/> <wsdl:message name="zoekPlaatsenResponse"> <wsdl:part element="impl:zoekPlaatsenResponse" name="parameters"/> <wsdl:message name="zoekPlaatsenRequest"> <wsdl:part element="impl:zoekPlaatsen" name="parameters"/> <wsdl:message name="getKaartenRequest"> <wsdl:part element="impl:getKaarten" name="parameters"/> <wsdl:portType name="CriminaliteitWSPortType"> <wsdl:operation name="zoekPlaatsen"> <wsdl:input message="impl:zoekPlaatsenRequest" name="zoekPlaatsenRequest"/> <wsdl:output message="impl:zoekPlaatsenResponse" name="zoekPlaatsenResponse"/> <wsdl:binding name="CriminaliteitWSPortSoapBinding" type="impl:CriminaliteitWSPortType"> <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="zoekPlaatsen"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="zoekPlaatsenRequest">
116
Bijlage E. De webservice - aanvullingen
117
<wsdlsoap:body use="literal"/> <wsdl:output name="zoekPlaatsenResponse"> <wsdlsoap:body use="literal"/> <wsdl:service name="CriminaliteitWS"> <wsdl:port binding="impl:CriminaliteitWSPortSoapBinding" name="CriminaliteitWSPort"> <wsdlsoap:address location="http://localhost:8084/CriminaliteitWS/ ...services/CriminaliteitWS"/>
WSDD-bericht <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="CriminaliteitWS" use="literal" style="wrapped" provider="java:RPC"> <parameter name="wsdlTargetNamespace" value="http://ws.web.criminaliteit.iii.hogent.be"/> <parameter name="wsdlServiceElement" value="CriminaliteitWS"/> <parameter name="wsdlServicePort" value="CriminaliteitWSPort"/> <parameter name="className" value="be.hogent.iii.criminaliteit.web.ws.CriminaliteitWSImpl"/> <parameter name="wsdlPortType" value="CriminaliteitWSPortType"/> <parameter name="typeMappingVersion" value="1.1"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="Session"/>
Bijlage E. De webservice - aanvullingen
118
qname="ns:Plaats" type="java:be.hogent.iii.criminaliteit.bo.Plaats" serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" encodingStyle="" />
<requestFlow> <parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/> <parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/> <parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/> <parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/> <parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/> <parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
WSDL-bericht na toevoeging Operation-tags aan WSDD <wsdl:definitions
Bijlage E. De webservice - aanvullingen targetNamespace="http://.../CriminaliteitWS/services/CriminaliteitWS" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://.../CriminaliteitWS/services/CriminaliteitWS" xmlns:intf="http://.../CriminaliteitWS/services/CriminaliteitWS" xmlns:tns1="http://ws.web.criminaliteit.iii.hogent.be" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <schema targetNamespace="http://ws.web.criminaliteit.iii.hogent.be" xmlns="http://www.w3.org/2001/XMLSchema"> <sequence> <element maxOccurs="unbounded" minOccurs="0" name="item" type="tns1:Plaats"/> <wsdl:message name="zoekPlaatsenResponse"> <wsdl:part name="Plaats" type="tns1:Plaats"/> <wsdl:message name="zoekPlaatsenRequest"> <wsdl:part name="naam" type="xsd:string"/> <wsdl:part name="nr" type="xsd:string"/> <wsdl:part name="actuelenaam" type="xsd:string"/> <wsdl:part name="type" type="xsd:string"/> <wsdl:part name="typeselected" type="xsd:boolean"/> <wsdl:portType name="CriminaliteitWSPortType"> <wsdl:operation name="zoekPlaatsen" parameterOrder="naam nr actuelenaam type typeselected"> <wsdl:input message="impl:zoekPlaatsenRequest" name="zoekPlaatsenRequest"/> <wsdl:output message="impl:zoekPlaatsenResponse" name="zoekPlaatsenResponse"/> <wsdl:binding name="CriminaliteitWSPortSoapBinding" type="impl:CriminaliteitWSPortType"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="zoekPlaatsen"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="zoekPlaatsenRequest">
119
Bijlage E. De webservice - aanvullingen <wsdlsoap:body namespace="http://ws.web.criminaliteit.iii.hogent.be" use="literal"/> <wsdl:output name="zoekPlaatsenResponse"> <wsdlsoap:body namespace="http://ws.web.criminaliteit.iii.hogent.be" use="literal"/> <wsdl:service name="CriminaliteitWS"> <wsdl:port binding="impl:CriminaliteitWSPortSoapBinding" name="CriminaliteitWSPort"> <wsdlsoap:address location=".../CriminaliteitWS/services/CriminaliteitWS"/>
120
Realisatie van de scriptie
Voor het maken van deze scriptie is gebruik gemaakt van volgende programma’s: • TEXnicCenter (http://www.toolscenter.org) • MiKTEX (http://www.miktex.org) • Paint.net(http://www.getpaint.net/) • Microsoft Office Visio Professional 2007 (http://office.microsoft.com/visio/) • Wink (http://www.debugmode.com/wink/)
121
Lijst van figuren
2.1 2.2
Het gecentraliseerd model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Het gedecentraliseerd model . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8
De De De De De De De De
5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12 5.13 5.14
Aanvullend databankschema versie 1 . . . . . . . . . . Aanvullend databankschema versie 2 . . . . . . . . . . Aanvullend databankschema versie 3 . . . . . . . . . . Aanvullend databankschema versie 4 . . . . . . . . . . Een voorbeeldhi¨erarchie van plaatsen . . . . . . . . . . De kaartenapplicatie versie 1 . . . . . . . . . . . . . . De kaartenapplicatie versie 2 . . . . . . . . . . . . . . Dialoogscherm om de naam van een kaart in te geven Dialoogscherm kaartselectie . . . . . . . . . . . . . . . Dialoogscherm kaarteigenschappen . . . . . . . . . . . Dialoogscherm om een plaats toe te voegen . . . . . . Dialoogscherm om een ouderplaats toe te voegen . . . Dialoogscherm om een ouderplaats te selecteren . . . . Dialoogscherm om een plaats te voegen . . . . . . . .
Misdrijf-Bron-rubriek . . . . . . . . . . . . . . . . . . Dader -rubriek . . . . . . . . . . . . . . . . . . . . . . . Slachtoffer(s)-rubriek . . . . . . . . . . . . . . . . . . . Straf -rubriek . . . . . . . . . . . . . . . . . . . . . . . rubrieken Terugkeer na verbanning, Varia en Statistiek tabel Vonnis en bijbehorende tabellen . . . . . . . . . tabel Persoon en Aanklacht . . . . . . . . . . . . . . . toevoeging van de archiefbronnen . . . . . . . . . . . .
122
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
6 7
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
11 12 12 13 14 16 18 19
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
27 28 29 30 31 35 36 37 38 38 39 39 40 40
Lijst van figuren
123
5.15 5.16 5.17 5.18 5.19
Dialoogscherm om een plaats te wijzigen . . . . . . . . De pakketstructuur . . . . . . . . . . . . . . . . . . . . De situatie bij een afbeelding kleiner dan de viewport Het tabelpaneel . . . . . . . . . . . . . . . . . . . . . . Een object van de DialogSelecteerWijzigPlaats-klasse
. . . . .
41 43 50 52 54
6.1 6.2 6.3
Het traditionele model voor een webapplicatie vergeleken met het Ajax-model Het compileerproces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De communicatie tussen de verschillende technologie¨en . . . . . . . . . . . . .
60 66 68
7.1
De cli¨ent-webserver communicatie . . . . . . . . . . . . . . . . . . . . . . . . .
70
8.1 8.2 8.3 8.4 8.5
De mappenstructuur van het project . . . De LinkBar . . . . . . . . . . . . . . . . . Het VonnisDataGrid -object . . . . . . . . Het popupvenster voor de archiefbronnen Het traject van een misdadiger . . . . . .
. . . . .
82 84 89 94 99
A.1 Een record uit de originele Access-databank . . . . . . . . . . . . . . . . . . .
104
B.1 Databankschema versie 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
106
D.1 De volledige databankstructuur . . . . . . . . . . . . . . . . . . . . . . . . . .
114
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Bibliografie
Apache Axis (2007). Axis User’s Guide. http://ws.apache.org/axis/java/user-guide. html. Articles Java (2007). Painting in AWT and Swing. http://java.sun.com/products/jfc/ tsc/articles/painting/index.html. Articles Java - Bharath Mundlapudi (2007). Implementing High Performance Web Services Using JAX-WS 2.0. http://java.sun.com/developer/technicalArticles/ WebServices/high_performance/. Articles Java - Ed Ort (2007). Java Architecture for XML Binding (JAXB). http://java. sun.com/developer/technicalArticles/WebServices/jaxb/index.html#runxmp. Bugs Database Java (2007a). Bugs Database Java. http://bugs.sun.com/bugdatabase/ view_bug.do?bug_id=4085329. Bugs Database Java (2007b). Bugs Database Java. http://bugs.sun.com/bugdatabase/ view_bug.do?bug_id=4465093. Documentation Postgres (2007). Unsupported Features. http://www.postgresql.org/ docs/8.2/interactive/unsupported-features-sql-standard.html. Evgen Potemkin (2007). SQL99 WITH patch. http://gppl.moonbone.ru/. J. J. Garrett (2005). Ajax: A new approach to web applications. http://www.adaptivepath. com/publications/essays/archives/000385.php. GlassFish (2007). GlassFish Community. https://glassfish.dev.java.net/public/ users.html#Learn_and_Write_Applications_with_the.
124
Bibliografie
125
Java Community Proces (2007a). JSR 109: Implementing Enterprise Web Services. http: //jcp.org/en/jsr/detail?id=109. Java Community Proces (2007b). JSR 181: Web Services Metadata for the JavaTM Platform . http://jcp.org/en/jsr/detail?id=181. A. Labs (2007). Showcase. http://labs.adobe.com/showcase/air/. J. Moreau (2007). Databanken. cursus Hogeschool Gent. Netbeans tutorial (2007). Web Services (JAX-WS) in Java EE 5. http://www.netbeans. org/kb/55/websvc-jax-ws.html. J. F. Nicolas C. Zakas, Jeremy McPeak (2006). Professional Ajax. Wrox. V. Ongenae (2006). Webtechnologie. cursus Hogeschool Gent. Postgres Forum (2007). recursive SQL and with clause. http://archives.postgresql.org/ pgsql-sql/2006-11/msg00160.php. psqlODBC (2007a). Official postgresql odbc driver. http://pgfoundry.org/projects/ psqlodbc/. psqlODBC (2007b). Postgresql. http://www.postgresql.org/. C. Schalk (2007). A hype-free introduction to ajax. http://www.oracle.com/technology/ pub/articles/schalk-ajax.html. J. Talbot (2006). Totaltraining flex 2. DVD. W3C (2007). Web Services Glossary. http://www.w3.org/TR/ws-gloss/. Wikipedia (2007). Ajax framework. http://en.wikipedia.org/wiki/Ajax_framework. Wikipedia (2007). Apache Axis. http://en.wikipedia.org/wiki/Apache_Axis.