Graafinen käyttöliittymä lintujen rengastusjärjestelmään



Samankaltaiset tiedostot
Graafinen käyttöliittymä lintujen rengastusjärjestelmään

TIETOKANTA MERIKOTKIEN SEURANTAAN Suunnitteludokumentti. Versiohistoria:

Graafinen käyttöliittymä lintujen rengastusjärjestelmään. Vaatimusdokumentti

TIETOKANTA MERIKOTKIEN SEURANTAAN Suunnitteludokumentti. Versiohistoria:

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

TIETOKANTA MERIKOTKIEN SEURANTAAN Suunnitteludokumentti. Versiohistoria: Helsinki,

Johdanto Javaan ja tietokantojen käsittelyyn Java Database Connectivity (JDBC)

Suunnitteludokumentti

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Suunnitteludokumentti

SQL-perusteet, SELECT-, INSERT-, CREATE-lauseet

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

WWW-sivut HTML-kielellä esitettyä hypertekstiaineistoa

HSMT Tietokannoista. Ville Leppänen. HSMT, c Ville Leppänen, IT, Turun yliopisto, 2008 p.1/32

Ohjelmoinnin jatkokurssi, kurssikoe

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Olio-ohjelmointi Javalla

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

Javan perusteita. Janne Käki

Esimerkkiprojekti. Mallivastauksen löydät Wroxin www-sivuilta. Kenttä Tyyppi Max.pituus Rajoitukset/Kommentit

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

Microsoft Visual Studio 2005

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Tehtävä 1. Tietojen lisääminen, poistaminen, päivittäminen ja tulostaminen

TIETOKANTA MERIKOTKIEN SEURANTAAN Toteutusdokumentti. Versiohistoria:

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Tietokannat II -kurssin harjoitustyö

Sisältö. 22. Taulukot. Yleistä. Yleistä

Helsingin yliopisto, TKTL Tietokantojen perusteet, k 2000 Tietokantaohjelmointi Harri Laine 1. SQL:n käyttö ohjelmissa

Sisältö. 2. Taulukot. Yleistä. Yleistä

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

1. Omat operaatiot 1.1

14. Poikkeukset 14.1

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Java ja tietokannan käsittely (JDBC)

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Tietokannat II -kurssin harjoitustyö

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Ohjelmoinnin perusteet Y Python

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

15. Ohjelmoinnin tekniikkaa 15.1

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

RDBMS - Yhteyskäytännöt

14. Poikkeukset 14.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Uutisjärjestelmä. Vaatimusmäärittely. Web-palvelujen kehittäminen. Versio 1.3

EXEC SQL BEGIN DECLARE SECTION

Ohjelmistojen mallintamisen ja tietokantojen perusteiden yhteys

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Asko Ikävalko, k TP02S-D. Ohjelmointi (C-kieli) Projektityö. Työn valvoja: Olli Hämäläinen

ITKP102 Ohjelmointi 1 (6 op)

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

Haaga-Helia/IltaTiko ict2tcd005: Ohjelmiston suunnittelutaito 1/7 Anne Benson. Tällä opintojaksolla käytämme VS:n kolmen kokonaisuuden luomiseen:

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

7. Näytölle tulostaminen 7.1

Java-API, rajapinnat, poikkeukset, UML,...

Ohjelmointi 2 / 2010 Välikoe / 26.3

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Ohjelmoinnin peruskurssi Y1

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

15. Ohjelmoinnin tekniikkaa 15.1

Attribuutit. Copyright IT Press Tämän e-kirjan kopiointi, tulostaminen ja jakeleminen eteenpäin luvatta on kielletty.

5. HelloWorld-ohjelma 5.1

ITKP102 Ohjelmointi 1 (6 op)

Java-kielen perusteet

HOJ Haja-aiheita. Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/10

Action Request System

Java-kielen perusteet

Harjoitustyö: virtuaalikone

Tietokantasovellus (4 op) - Web-sovellukset ja niiden toteutus

13. Loogiset operaatiot 13.1

Teknillinen korkeakoulu T Tietojenkäsittelyopin ohjelmatyö. Testitapaukset - Koordinaattieditori

Graafinen käyttöliittymä lintujen rengastusjärjestelmään

Kehitysohje. ETL-työkalu. ExtraTerrestriaLs / Aureolis Oy

Määrittelydokumentti NJC2. Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

Ylläpito-ohje. Matematiikan oppifoorumi. Carl Johansson Jukka Kariola Outi Marttila Helena Venäläinen Sampsa Virtanen. Ohjaaja.

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Työ tehdään itsenäisesti yhden hengen ryhmissä. Ideoita voi vaihtaa koodia ei.

9. Periytyminen Javassa 9.1

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Toteutusdokumentti. Kotkat-ryhmä. Helsinki Ohjelmistotuotantoprojekti HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos

Insert lauseella on kaksi muotoa: insert into taulu [(sarakenimet)] values (arvot)

16. Javan omat luokat 16.1

JReleaser Yksikkötestaus ja JUnit. Mikko Mäkelä

Arkkitehtuurikuvaus. Ratkaisu ohjelmistotuotelinjan monikielisyyden hallintaan Innofactor Oy. Ryhmä 14

RATKI 1.0 Käyttäjän ohje

Tässä tehtävässä käsittelet metodeja, listoja sekä alkulukuja (englanniksi prime ).

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

Kirjasto Relaatiotietokannat Kevät Auvinen Annemari Niemi Anu Passoja Jonna Pulli Jari Tersa Tiina

Ohjelmoinnin perusteet Y Python

Taulukot. Jukka Harju, Jukka Juslin

Transkriptio:

Graafinen käyttöliittymä lintujen rengastusjärjestelmään Toteutusdokumentti Versiohistoria: 0.1 23.11.2000 Ensimmäinen luonnos. 0.2 27.11.2000 Toinen luonnos. 0.3 4.12.2000 Kolmas luonnos. 0.9 7.12.2000 Olennaisilta osin valmis versio ryhmän tarkastusta varten. Muutamia välitekstejä ja tarkennuksia puuttuu. 1.0 11.12.2000 JavaScript-osat lisätty. Tekstiä korjailtu. 1.1 14.12.2000 Toiminnan kuvausta parannettu. Muutamia korjauksia. Helsinki, 14. joulukuuta 2000 Lincoyan Kekki, Jaakko Kiviluoto, Juha Markkanen, Juha Niemi, Joona Palaste, Janne Pasanen HELSINGIN YLIOPISTO Tietojenkäsittelytieteen laitos Ohjelmistotuotantoprojekti Tipu4

Sisältö 1 Johdanto 1 1.1 Tuotteen tausta ja tarkoitus..................... 1 1.2 Erikoissanasto............................ 1 1.3 Yleiskatsaus dokumenttiin..................... 2 2 Vaatimusmäärittely, suunittelu ja niihin tehdyt muutokset 3 2.1 Vaatimusmäärittely......................... 3 2.2 Suunnittelu............................. 3 2.2.1 Tietokantaluokat...................... 3 2.2.2 Painikkeet.......................... 4 2.2.3 Virheilmoitukset...................... 5 3 Toteutusratkaisut 5 3.1 Ratkaisun filosofia ja toteutuksen toimintaperiaate......... 5 3.2 Käytetyt ohjelmointikielet..................... 7 3.3 Käytetyt luokkakirjastot ja ajurit.................. 7 3.4 Sisäinen kommunikointi...................... 7 3.5 Sivujen mallipohjat......................... 9 3.5.1 Yleinen rakenne...................... 9 3.5.2 Tila normal........................ 11 3.5.3 Tila detail........................ 11 3.5.4 Tila list.......................... 12 3.5.5 Tila record_added................... 12 3.5.6 Apurekisterit - Kunnat................... 13 3.5.7 Renkaat - Renkaiden jakelu................ 13 3.5.8 Rengastukset - Syöttö................... 13

2 3.5.9 Rengastukset - Ylläpito................... 14 3.6 JavaScript-osat........................... 14 3.6.1 Valikko........................... 14 3.6.2 Lomakkeiden yhteiset funktiot............... 15 3.6.3 Lomakekohtaiset funktiot................. 17 3.7 Pakkaus tipu.db......................... 19 3.7.1 Yhtäaikaisuus........................ 19 3.7.2 Luokka tipu.db.connectionpool......... 21 3.7.3 Luokka tipu.db.table................ 26 3.7.4 Sisäluokka Table.FieldInfo............. 31 3.7.5 Luokka tipu.db.databaseoperation....... 32 3.7.6 Luokka OperationResults.............. 34 3.7.7 Luokka tipu.db.searchoperation........ 36 3.7.8 Luokka tipu.db.searchresults.......... 38 3.7.9 Luokka tipu.db.insertoperation........ 41 3.7.10 Luokka tipu.db.updateoperation........ 42 3.7.11 Luokka tipu.db.deleteoperation........ 44 3.8 Pakkaus tipu.general..................... 45 3.8.1 Luokka tipu.general.check............ 45 3.8.2 Luokka tipu.general.coords............ 46 3.8.3 Luokka tipu.general.properties........ 48 3.9 Pakkaus tipu.servlet..................... 50 3.9.1 Luokka tipu.servlet.tipuservlet........ 50 3.9.2 Luokka tipu.servlet.kunnat............ 56 3.9.3 Luokka tipu.servlet.resyotto.......... 58 3.9.4 Luokka tipu.servlet.reyllapito........ 64

3 3.9.5 Luokka tipu.servlet.rejakelu.......... 66 3.9.6 Luokka tipu.servlet.popup............ 70 4 Testaus 73 4.1 Käyttöliittymä............................ 74 4.2 Tietokantaluokat.......................... 75 4.2.1 Luokka tipu.db.table................ 75 4.2.2 Luokka tipu.db.connectionpool......... 76 4.2.3 Luokka tipu.db.databaseoperation....... 77 4.2.4 Luokka tipu.db.searchoperation........ 78 4.2.5 Luokka tipu.db.insertoperation........ 80 4.2.6 Luokka tipu.db.updateoperation........ 81 4.2.7 Luokka tipu.db.deleteoperation........ 82 4.2.8 Luokka tipu.db.operationresults........ 83 4.2.9 Luokka tipu.db.searchresults.......... 83 4.3 Yleiset luokat............................ 84 4.3.1 Luokka tipu.general.check............ 84 4.3.2 Luokka tipu.general.coords............ 84 4.4 Servletit ja käyttöliittymä...................... 85 4.4.1 Luokka tipu.servlet.kunnat............ 85 4.4.2 Luokka tipu.servlet.resyotto.......... 88 4.4.3 Luokka tipu.servlet.reyllapito........ 92 4.4.4 Luokka tipu.servlet.rejakelu.......... 94 4.4.5 Luokka tipu.db.popup................ 97 4.5 JavaScript-komponentit....................... 98 5 Jatkokehitys 100 5.1 Toteuttamatta jääneet piirteet.................... 100

4 A GNU Library General Public License, Version 2 101 Viitteet 111

Kuvat 1 Yleiskuva WWW-Tipu -käyttöliittymästä.............. 6 2 Esimerkki hakuoperaation aikana tapahtuvasta viestinästä..... 8 3 Mallipohjan looginen rakenne.................... 10 4 Pakkauksen tipu.db luokkakaavio................ 20

1 1 Johdanto 1.1 Tuotteen tausta ja tarkoitus Rengastustoimistossa käytetään SQL*Forms sovelluskehittimellä toteutettua käyttöliittymää tipukantaan. Oracle on kuitenkin ilmoittanut, ettei se enää tue SQL*Forms-sovelluksia. Rengastustoimisto on tehnyt päätöksen korvata vanha käyttöliittymä uudella. Projektin tarkoituksena on suunnitella ja toteuttaa graafinen käyttöliittymä lintujen rengastusjärjestelmään. Tavoitteena on korvata vanha merkkipohjainen käyttöliittymä graafisella (eli ei merkkipohjaisella). Tuotantokäytössä olevasta järjestelmästä on tarkoitus käyttää hyväksi Fortran-kielisiä ohjelman osia. 1.2 Erikoissanasto CHECK Connection pool HTML HTTP JDBC-ajuri Kontrolli Asiakkaan Fortran ohjelma joka hoitaa tiettyjen tietojen tarkistuksia. Ohjelmistoratkaisu, jolla voidaan rajata yhteydet johonkin tiettyyn resurssiin. Näitä yhteyksiä luodaan ja jaetaan asiakkaille dynaamisesti. Hypertext Markup Language on internetissä käytetty tiedon esitystapa, jota asiakaspään selaimet osaavat tulkata. Hypertext Transfer Protocol on WWW-liikenteen protokolla. Java DataBase Connectivity -ajuri, mahdollistaa tietokantakutsut palvelinsovelmista. Rengastajien suorittama rengastetun linnun tapaaminen. Kontrollit tehdään pesiltä lintuja pyydystämällä tai esimerkiksi kiikaroinnilla.

2 Löytö Mallipohja Rengastaja Rengastus Servlet SQL Tapaaminen Template Tipukanta WWW-Tipu Ennestään rengastetun linnun kohtaaminen. Lintu voi olla elävä tai kuollut. Dokumentin runko, joka sisältää tietyllä tavalla merkittyjä kohtia, joihin voidaan ohjelmallisesti lisätä vaihtuvia arvoja. Lintuharrastaja joka on oikeutettu ja koulutettu merkitsemään lintuja virallisilla linturenkailla. Uuden renkaan laittaminen linnulle, jolla ei ennestään ole rengasta. Javalla koodattu ohjelmamoduuli, joka suoritetaan palvelimella, palvelinsovelma. Structured Query Language. Standardi kieli, jolla voidaan määrittää erilaisia tietokantaoperaatioita. Ennestään rengastetun, elävän linnun kohtaaminen. Dokumentin runko, joka sisältää tietyllä tavalla merkittyjä kohtia, joihin voidaan ohjelmallisesti lisätä vaihtuvia arvoja. Rengastustoimistolla käytettävä relaatiotietokanta rengastusten ja niihin liittyvien tietojen säilyttämiseen. Tässä dokumentissa kuvattu graafinen käyttöliittymä lintujen rengastustietokannalle. 1.3 Yleiskatsaus dokumenttiin Tässä dokumentissa käydään läpi WWW-Tipun toteutus. Luvussa kaksi mainitaan vaatimusmäärittelyyn ja suunittelluun tehdyt muutokset, lisäykset ja tarkennukset. Luvussa kolme kerrotaan miten ja millä WWW-Tipu on tehty. Ohjelman Java-kielisten osien lähdekoodin Javadoc-dokumentaatio löytyy projektin WWW-sivuilta osoitteesta http://www.cs.helsinki.fi/gro-

3 up/tipu4/javadoc/. Ohjelman lähdekoodin mukana seuraa myös työkalut ko. dokumentaation tuottamiseen uudelleen. 2 Vaatimusmäärittely, suunittelu ja niihin tehdyt muutokset Järjestelmän vaatimukset ovat määritelty dokumentissa Graafinen käyttöliittymä lintujen rengastusjärjestelmään Vaatimusdokumentti (versio 1.1) [4]. Suunnittelu on kuvattu dokumentissa Graafinen käyttöliittymä lintujen rengastusjärjestelmään Suunnitteludokumentti (versio 1.2) [3]. 2.1 Vaatimusmäärittely Vaatimusmäärittelyyn ei ole tehty muutoksia. 2.2 Suunnittelu Suunnitteluun on tehty seuraavat muutokset / lisäykset: 2.2.1 Tietokantaluokat Seuraavien luokkien toteutus poikkeaa suunnitellusta: Table Luokka tietää tauluista myös attribuuttien tyypit sekä sen, voiko attribuutti saada NULL -arvoa. Taulun kommentteja ei tässä toteutuksessa käytetä HTML-sivuilla kenttien pika-avusteina. Luokan avuksi on lisätty tietorakenne Table.FieldInfo.

4 OperationResults Instanssin rajapinta on muutettu käytännöllisemmäksi korvaamalla metodit setsuccess(boolean) ja seterrormessage(string) metodilla seterror(string), joka automaattisesti asettaa virhetilan. SearchOperation Operaatio on muista poiketen toteutettu dynaamisesti koottavin SQL-lausein. Valmisteltuja lausekkeita olisi käytännössä mahdotonta käyttää luontevasti. Metodin getsqlstring toteutus ottaa arvokseen hakuparametrit, eikä metodia setparameters käytetä lainkaan. Haku etsii nyt ainoastaan ROWID:t, ja välittää ne luokalle SearchResults. Hakukursoria ei jätetä auki. SearchResults Luokka ei sisällä haun ResultSet -kursoria, vaan ainoastaan selattavan listan löydettyjen rivien ROWID:stä. Näiden perusteella rivien sisältö haetaan erillisellä haulla aina metodia getabsoluterow (suoraan tai epäsuorasti metodeilla getpreviousrow, getnextrow tai getcurrentrow) kutsuttaessa. 2.2.2 Painikkeet Luvussa 5.2.3 määriteltyihin lomakkeen painikkeisiin on tehty seuraavat muutokset: Päivitä Painikkeen teksit on muuttunut, uusi teksti on Korjaa. Tyhjennä lomake Painikkeen teksti on muuttunut, uusi teksti on Tyhjennä. Kaksi uutta painiketta ovat: Palauta Palauttaa kenttien arvoiksi ne jotka niissä olivat kun sivu ladattiin. Eli peruu kenttiin tehdyt muutokset (mukaanlukien lomakkeen tyhjennyksen Tyhjennä -painikkeella).

5 Ohje Siirtyy näytön ohjesivulle. 2.2.3 Virheilmoitukset Luvussa 5.2.5 määriteltyihin virheilmoituksiin on tehty seuraavat muutokset: Haun tuloksia ei löydy Istuntoa ei ole käytetty ja on siksi aikakatkaistu. Tee uusi haku. Hakuehdoilla ei löytynyt mitään Yksikään tietue ei täyttänyt hakuehtoja. Tarkista hakuehdot. Lisäys ei onnistunut Insert operation exception: ORA-00001: yksikäsitteistä rajoitetta (... ) loukattu. Lisättävässä tietueessa on joku ristiriitaisuus olemassaolevien tietueiden kanssa. Tarkista avainkenttien tiedot. 3 Toteutusratkaisut 3.1 Ratkaisun filosofia ja toteutuksen toimintaperiaate Vaikka kaikki näytöt ovat hyvin samanlaisia niin joissakin näytöissä on erityspiirteitä joiden takia päädyttiin siihen, että jokaista näyttöä vastaa yksi servletti. Luokkarakenteessa tämä on toteutettu siten että näyttöjen servletit periytyy yhteisestä abstraktista yliluokasta. Helpot tarkistukset jotka voidaan tehdä selaimessa, tehdään siellä. Nämä ovat kenttien syntaksin tarkistus, eli esimerkiksi onko pakolliset kentät täytetty ja sisältääkö numeeriset kentät ainoastaan numeroita. Koordinaattien muunnos oli pakko toteuttaa servleteissä Javascriptin laskentafunktioiden epätarkkuuden vuoksi.

6 Kuva 1: Yleiskuva WWW-Tipu -käyttöliittymästä. Palvelinkoneella, alkokrunni.cs.helsinki.fi (alias db.cs.helsinki.fi), pyörii Apache WWW-palvelin sekä JServ servlet-palvelin. Kuvassa 1 on WWW-Tipu:n perusrakenne. Tärkeimpiä elementtejä ovat: Apache JServ -pari, joka HTTP-protokollaa käyttäen kommunikoi asiakkaan selaimen kanssa. Servletit jotka saavat tulostamiensa HTML-sivujen rungot.template -tiedostoista, ja esitettävät tiedot Tipukannasta. Huomaa kommunikointi CHECK-ohjelman kanssa: Servlet ajaa CHECK:in suoraan, mutta lukee tämän antaman palautteen tietokannasta, johon CHECK sen kirjoittaa. Staattiset elementit eli.js- (JavaScript) ja.html -tiedostot.

7 3.2 Käytetyt ohjelmointikielet Servletit kirjoitettiin Java 1.2 -standardin mukaisesti. Lähdekoodi kommentoitiin siten, että siitä voidaan generoida Javadoc [7] -dokumentaatio [2]. Selaimessa tehtävät tarkistukset ja alasvetovalikko on toteutettu JavaScriptillä. 3.3 Käytetyt luokkakirjastot ja ajurit Templatet on toteutettu FreeMarker-kirjastolla [1], joka on julkaistu GNU General Public License:n (ks. liite A) alla. Alasvetovalikko on toteutettu osoitteesta http://www.brainjar.com saatavissa olevalla Navigation Bar -sovelluksella, jonka lähdekoodi ja dokumentaatio ovat tiedostossa navbar.zip. Oracle 8i:n JDBC-ajurista on käytössä versio 8.1.7 for Java 1.2.x. 3.4 Sisäinen kommunikointi WWW-Tipu perustuu periaatteessa kolmen eri tahon asiakkaan selaimen, Servlet-palvelimen ja Tietokantapalvelimen väliseen vuorovaikutukseen. Jokaisessa instanssissa on yksi tai useampi komponentti ja näillä on kullakin tietty palveluhierarkiansa. Kuva 2 näyttää esimerkkinä Kunnat-servletillä suoritetun hakuoperaation aiheuttamat toimenpiteet. Nämä ovat tapahtumajärjestyksessä: 1. Käyttäjä tekee uuden haun lähettämällä Kunnat-servletille HTTP-pyynnön. 2. Kunnat-servlet konstruoi SearchOperation -olion hakua varten.

8 käyttäjä 1. Kunnat servlet 2. Search Operation Search Results 3. Oracle JDBC 5. 4. 9. 13. 10. 6. 8. 11. 12. 7. Kuva 2: Esimerkki hakuoperaation aikana tapahtuvasta viestinästä. 3. SearchOperation -olio tekee JDBC:n yli SQL-kyselyn. 4. JDBC-ajuri palauttaa SearchOperation-oliolle hakutulokset ResultSet -oliona (ResultSet -oliota ei erikseen näytetä kuvassa). 5. SearchOperation -olio konstruoi hakutuloksista uuden SearchResults -olion. 6. Kunnat-servlet saa SearchOperation -oliolta paluuarvonaan konstruoidun SearchResults -olion ja arkistoi sen. 7. Kunnat-servlet hakee ensimmäisen hakutulossivun SearchResults -oliosta. 8. SearchResults-olio palauttaa ensimmäisen hakutulossivun sisällön Kunnatservletille Row-olioina (Row-olioita ei erikseen näytetä kuvassa). 9. Kunnat-servlet lähettää ensimmäisen hakutulossivun käyttäjälle HTMLlomakkeessa.

9 10. Käyttäjä selaa seuraavalle hakutulossivulle lähettämällä Kunnat-servletille uuden HTTP-pyynnön. 11. Kunnat-servlet hakee seuraavan hakutulossivun arkistoimastaan SearchResults -oliosta. 12. SearchResults -olio palauttaa seuraavan hakutulossivun sisällön Kunnat-servletille Row-olioina (ei erikseen näytetä kuvassa). 13. Kunnat-servlet lähettää seuraavan hakutulossivun käyttäjälle HTMLlomakkeessa. 3.5 Sivujen mallipohjat Servlet-luokkien tuottamat tulosteet muunnetaan asiakkaan ymmärtämään HTML-muotoon FreeMarker-mallipohjien (.template -tiedostot) avulla. Kullekin servletille on toteutettu sen ohjaama mallipohja. Tässä luvussa kuvataan mallipohjien yleinen rakenne sekä mahdolliset servlet-kohtaiset erityisratkaisut. 3.5.1 Yleinen rakenne Mallipohjat sisältävät tavallista HTML-koodia sekä FreeMarker-komentoja, joiden avulla on toteutettu mm. ehtorakenteet ja luettelomuotoisen tiedon esitys. HTML-lomakkeissa käytetyt kenttien ja FreeMarker-muuttujien nimet vastaavat (soveltuvilta osin) tietokannan taulun sarakkeiden nimiä. Lisätietoja FreeMarkermallipohjien kieliopista on dokumentissa [8]. Ylimmän tason mallipohjalogiikkaa ohjataan formstate -muuttujan avulla. Muuttujalla on neljä tilaa: detail, list, normal (implisiittinen) ja record_added. Seuraavassa esitellään kaikille tiloille yhteiset muuttujat ja rakenteet sekä jäljempänä omina kohtinaan mahdolliset tilakohtaiset lisätiedot.

10 Kuva 3: Mallipohjan looginen rakenne. Seuraavassa taulukossa kuvataan tilojen yhteiset muuttujat: Muuttuja action errors noticeheading notices result.* rowid static_url success Sisältö Edellinen toteutettu pyyntö Edellisen pyynnön aiheuttamien virheiden luettelo Muiden ilmoitusten otsikko Muiden ilmoitusten luettelo Sisältää hakutuloksen (detail-tila) tai hakuehdot Lomakkeella käsiteltävän olion tunniste Staattisten sivujen URL (nyt http://www.cs.helsinki.fi/group/tipu4/www-tipu/) Tosi, jos edellisen pyynnön toteutus onnistui Mallipohjan yleinen rakenne ilmenee kuvasta 3. Staattinen HTML-koodi on valkoisella, ehto- ja tilalohkot harmaalla pohjalla. Yksityiskohtaisempi toteutus ilmenee kommentoidusta lähdekoodista.

11 3.5.2 Tila normal Mallipohja on tilassa normal, kun se ei ole missään muussa tilassa. Formstatemuuttujalla ei siis ole koskaan varsinaisesti tätä arvoa. Tämä on lomakkeen alkutila, kun käyttäjä avaa servletin tarjoaman lomakesivun ensimmäisen kerran. Tässä tilassa käyttäjä voi hakea ja lisätä tietueita. 3.5.3 Tila detail Tila on käytössä, kun käyttäjälle näytetään yksittäistä hakutulosta. Tähän tilaan siirrytään suoraan, jos haun tuloksena löytyy vain yksi hakua vastaava tietue. Jos tulosjoukossa on useampia tietueita, käyttäjällä on mahdollisuus selata sitä eteenja taaksepäin vastaavilla HTML-sivun linkeillä. Käyttäjä voi halutessaan päivittää näkyvissä olevaa tietuetta tai poistaa sen. Seuraavassa taulukossa kuvataan tilakohtaiset muuttujat: Muuttuja searchresults.currentindex searchresults.nextexists searchresults.previousexists searchresults.totalfound Sisältö Näkyvissä olevan tietueen järjestysnumero Tosi, jos tulosjoukossa on nykyistä tietuetta seuraava tietue Tosi, jos tulosjoukossa on nykyistä tietuetta edeltävä tietue Haun palauttamien tietueiden kokonaismäärä

12 3.5.4 Tila list Tila on käytössä, kun käyttäjälle näytetään hakuoperaation tulosluettelo, eli haku on palauttanut useamman kuin yhden tuloksen tai käyttäjä on palannut detailtilasta hakuehtoihin. Tässä tilassa käyttäjä voi selata tulosluetteloa, avata haluamansa tietueen tai tehdä uuden haun. Seuraavassa taulukossa kuvataan tilakohtaiset muuttujat: Muuttuja Sisältö page.pagenumber Yksittäisen luettelosivun numero pages Hakutulosten sivuluettelo searchresults.currentpage Näkyvissä olevan sivun numero searchresults.firstdisplayed Ensimmäisen näytetyn hakutuloksen numero searchresults.lastdisplayed Viimeisen näytetyn hakutuloksen numero searchresults.nextpageexists Tosi, jos nykyinen sivu ei ole viimeinen searchresults.previouspageexists Tosi, jos nykyinen sivu ei ole ensimmäinen searchresults.results Hakutulosluettelo searchresults.totalfound Haun palauttamien tietueiden määrä 3.5.5 Tila record_added Tila record_added on käytössä, kun käyttäjä on lisännyt tietokantaan uuden tietueen Lisää-painikkeella. Tilan ansiosta on mahdollista muokata lisättyä tietuet-

13 ta tai poistaa se, vaikka käytössä ei ole aktiivista tulosjoukkoa. 3.5.6 Apurekisterit - Kunnat Mallipohja kunnat.template on toteutettu suoraan yleisen pohjan avulla eikä oletusrakenteesta ole poikettu. Seuraavassa taulukossa kuvataan mallipohjakohtaiset muuttujat: Muuttuja kunta.* laanit lyl_alueet Sisältö Näkyvissä olevan kunnan tiedot Tietokannassa olevat läänit Tietokannassa olevat lyl-alueet 3.5.7 Renkaat - Renkaiden jakelu Mallipohja rejakelu.template on toteutettu suoraan yleisen pohjan avulla eikä oletusrakenteesta ole poikettu. jakelu on mallipohjan ainoa muuttuja. Se sisältää näkyvissä olevan jakelun tiedot. 3.5.8 Rengastukset - Syöttö Mallipohja resyotto.template on toteutettu suoraan yleisen pohjan avulla eikä oletusrakenteesta ole poikettu. Mallipohjakohtaiset muuttujat:

14 Muuttuja rengas.* sanasto.* lintuas Sisältö Näkyvissä olevan rengastuksen tiedot Sanastotaulusta haettavat arvot (numero muuttujan lopussa viittaa koodiin, jolla arvot haetaan sanastotaulusta) Tietokannassa olevat lintuasemat 3.5.9 Rengastukset - Ylläpito Mallipohja reyllapito.template on toteutettu suoraan yleisen pohjan avulla eikä oletusrakenteesta ole poikettu. Mallipohjakohtaiset muuttujat: Muuttuja rengas.* sanasto.* lintuas Sisältö Näkyvissä olevan rengastuksen tiedot Sanastotaulusta haettavat arvot (numero muuttujan lopussa viittaa koodiin, jolla arvot haetaan sanastotaulusta) Tietokannassa olevat lintuasemat 3.6 JavaScript-osat WWW-Tipussa käytetään JavaScriptiä kahteen tarkoitukseen: valikon toteutukseen ja lomakkeiden käsittelyyn. 3.6.1 Valikko Tässä kappaleessa käsitellään WWW-Tipua varten alasvetovalikon 3.3 koodiin tehdyt lisäykset ja muutokset. Itse ydinsovelluksen dokumentaatio on HTMLmuodossa tiedostossa navbar.zip.

15 Alkuperäisessä toteutuksessa toimitetaan sekä pakattu navcond.js että erillisinä dhtmllib.js ja menu.js, joista se on muodostettu. Selkeyden vuoksi navcond.js on nimetty WWW-Tipua varten menu.js:ksi. Tiedosto menuitems.js on staattinen Javascript-kirjasto, joka sisältää WWW- Tipu valikko-objektin ominaisuudet ja vaihtoehdot. Tiedosto sisältää Navigation Bar -ohjeen sisältämien osien lisäksi seuraavat muuttujat, joita on muutettava, jos WWW-Tipu siirretään eri URL-osoitteeseen: servleturl staticurl WWW-Tipun servlet-palvelimen URL WWW-Tipun staattisten HTML-sivujen URL 3.6.2 Lomakkeiden yhteiset funktiot functions.js on staattinen Javascript-kirjasto, joka sisältää WWW-Tipun lomakkeiden käsittelyssä käytetyt yhteiset funktiot. Tässä esitellään lyhyesti kukin kirjaston funktio. clearfields() Funktio tyhjentää kaikkien kutsuparametrina välitettyjen lomakekenttien sisällön. Syöte: 1 n kenttäobjektia. copyif(field1, field2) Funktio kopioi ensimmäisen kentän arvon toiseen, jos toinen on tyhjä. Syöte: Kenttäobjekti, kenttäobjekti.

16 clearform(form) Tyhjentää form-objektin mukaisen lomakkeen valintaruudut, -napit, piilo- ja syöttökentät. Syöte: Lomakeobjekti. formisempty(form) Tutkii, onko form-objektilla välitetty lomake tyhjä. Syöte: Lomakeobjekti. Palauttaa: true jos lomake on tyhjä. false jos lomake ei ole tyhjä. popup(column, field, title) Avaa kuvaruutuun uuden ikkunan yksittäisen lomakekentän arvon hakua varten. Syöte: Tietokannan sarakkeen nimi, kohdekentän nimi, ikkunan otsikko. selectfield(field) Siirtää kohdistimen field-objektin mukaiseen kenttään ja maalaa kentän sisällön. validdate(field) Tarkistaa, onko field-objektin mukaisen syöttökentän sisältönä kelvollinen päivämäärä ja lisää siihen tarvittaessa etunollat.

17 Syöte: Lomakkeen syöttökenttäobjekti. Palauttaa: true jos kentän sisältö on kelvollinen päivämäärä. false jos kentän sisältö ei ole kelvollinen päivämäärä. validfloat(floatstr) Tarkistaa, onko floatstr-merkkijono kelvollinen liukuluku. Syöte: Merkkijono. Palauttaa: true jos merkkijono on kelvollinen liukuluku. false jos merkkijono ei ole kelvollinen liukuluku. validint(intstr) Tarkistaa, onko intstr-merkkijono kelvollinen kokonaisluku. Syöte Merkkijono. Palauttaa: true jos merkkijono on kelvollinen kokonaisluku. False jos merkkijono ei ole kelvollinen kokonaisluku. 3.6.3 Lomakekohtaiset funktiot Käyttäjän täyttämille lomakkeille tehdään toimintokohtainen esitarkistus JavaScript-funktioilla ennen lomakkeen sisällön ja käyttäjän määrittämän pyynnön lähetystä servletille. Näin käyttäjä saa palautteen virheellisesti täytetyistä

18 kentistä mahdollisimman nopeasti, eikä palvelinta tai tietoliikenneverkkoa tarvitse kuormittaa turhaan. Kunkin lomakkeen käyttämän JavaScript-tiedoston nimi on sama kuin lomakkeen FreeMarker-mallipohjan. Esimerkiksi kunnat.template -mallipohja käyttää siis kunnat.js -tiedostoa. Jokainen lomakkeen lähetystä vaativa painike kutsuu tiedostossa olevaa submitform-funktiota, joka tekee lomakkeessa oleville tiedoille tarvittavat tarkistukset. Seuraavassa kuvataan lomakekohtaisen mallipohjan yleiset funktiot: commonchecksok(form) Funktio tarkistaa, ovatko lomakkeen kenttien arvot kelvollisia lisäys- ja päivitysoperaatiossa. Syöte: Lomakeobjekti. Palauttaa: true jos kaikkien tarkistettujen kenttien arvot ovat OK. false jos jonkin kentän arvo on virheellinen. submitform(action, form) Funktio tarkistaa, ovatko lomakkeen kenttien arvot kelvollisia pyydetylle toiminnolle. Jos arvot ovat OK, lähettää lomakkeen servletille käsiteltäväksi. Syöte: Toimintomerkkijono, lomakeobjekti.

19 3.7 Pakkaus tipu.db JDBC-yhteys servleteistä tietokantaan hoidetaan tipu.db pakkauksen luokkien avulla. Yhteyksiä jaetaan yhteysvarannon (connection pool) avulla. Täten mahdollisesti monia muitakin käyttäjiä palvelevaa tietokantaa ei missään vaiheessa ruuhkauteta tiettyä rajaa enempää. Kuva 4 esittää pakkauksen luokkakaavion. Luokkaa ConnectionPool ei ole merkitty. Sitä käytetään ainoastaan resurssiluokkana. Tietokantaan voi kohdistua neljä erilaista operaatiota: haku, lisäys, muutos tai poisto. Nämä operaatiot on toteutettu luokissa SearchOperation, InsertOperation, DeleteOperation ja UpdateOperation. Nämä kaikki periytyvät yhteisestä abstraktista kantaluokasta DatabaseOperation (ks. kuva 4, joka käytännössä tarjoaa tuen ConnectionPool:in dynaamiselle yhteysjaolle. Operaatiot palauttavat kutsujalleen OperationResults -objektin, josta nähdään onnistuiko suoritettu operaatio, ja josta voidaan tarvittaessa noutaa operaation virheilmoitus. Onnistunut hakuoperaatio palauttaa erillisen SearchResults -objektin, jonka avulla hakutuloksia voidaan selata. 3.7.1 Yhtäaikaisuus Käyttöliittymän toteutusalustan (HTTP) yhteydettömästä luonteesta johtuen ei kannasta voida varata resursseja yhden pyynnön käsittelyä pidemmäksi ajaksi. Palvelin ei käytännössä voi varmasti tietää milloin jokin käyttäjä on lopettanut kannan käytön, esim. vain sulkemalla selainikkunansa.

DatabaseOperation #result: OperationResults -statementmap: Map #table: Table #DatabaseOperation(table:Table) #getpreparedstatement(con:connection): PreparedStatement #getresultsetconcurrency(): int #getresultsettype(): int #getsqlstring(): String Table.FieldInfo +type: int +key: boolean +nullable: boolean +comment: String Table -ATTRIBUTE_SEPARATOR: String = ";" -FIELD_SEPARATOR: String = ":" -attributes: SortedSet -fields: Map -tabledata: Map -tablename: String +gettable(tablename:string): Table +getattributes(): SortedSet +getkeys(): SortedSet +gettype(attr:string): int +getcomment(attr:string): String +iskey(attr:string): boolean +isnullable(attr:string): boolean -Table(tableName:String,attributes:SortedSet,fields:Map) -loadtabledata(): Map -parsefield(fields:stringtokenizer): Table.FieldInfo InsertOperation +InsertOperation(table:Table) +executeinsert(con:connection,values:map): OperationResults #getsqlstring(): String #setparameters(stmt:preparedstatement,rowid:string): void -stringtodate(strdate:string): Date DeleteOperation +DeleteOperation(table:Table) +executedelete(con:connection,rowid:string): OperationResults #getsqlstring(): String +setparameters(stmt:preparedstatement,rowid:string): void OperationResults -errormessage: String -succeeded: boolean +OperationResults() +OperationResults(msg:String) +geterrormessage(): String +seterror(msg:string): void +seterrormessage(msg:string): void +succeeded(): boolean UpdateOperation -dateformat: SimpleDateFormat = yyyymmkkmmss +UpdateOperation(table:Table) +executeupdate(con:connection,values:map,rowid:string): OperationResults #setparameters(stmt:preparedstatement,rowid:string): void #getsqlstring(): String SearchOperation +SearchOperation(table:Table) +executesearch(con:connection,values:map): OperationResults +executesearch(con:connection,values:map,sortkey:string): OperationResults #getsqlstring(): String #getsqlstring(values:map,sortkey:string): String -escapequotes(s:string): String SearchResults -currentrow: int -rowlist: Vector -table: Table -dateformat: SimpleDateFormat = "dd.mm.yy" +SearchResults(rs:ResultSet,t:Table) +getabsoluterow(rownum:int): SortedMap +getabsoluterow(rowid:string): SortedMap -getabsoluterow(rowid:rowid): SortedMap +getnextrow(): SortedMap +getprevoiusrow(): SortedMap +getrowcount(): int +getrownumber(): int +removerow(rowid:rowid): void +removerow(rowid:string): void -setresultset(rs:resultset): void 20 Kuva 4: Pakkauksen tipu.db luokkakaavio.

21 Tästä syystä mm. edellisen käyttöliittymän ominaisuutta, joka lukitsi esillä olevan tietueen muokkausta varten, ei nyt voitu toteuttaa. Seurauksena saattaa olla esim. peräkkäisten päivitysten ongelma: Kun kaksi käyttäjää muokkaa samaa tietuetta ja kumpikin tallettaa sen yhtäaikaa. Tapahtumien onnistuminen riippuu tietokannan transaktioiden eristäytyneisyysasteesta ja toimintamallista tilanteissa, joissa yritetään operoida lukittua tietuetta. WWW-Tipu asettaa jokaiselle toimittamalleen tietokantaoperaatiolle sen vaatiman eristäytyneisyysasteen ja toimii operaatioiden antaman palautteen perusteella. Mikäli esim. päivitys epäonnistuu, tästä ilmoitetaan käyttäjälle, ja tarjotaan mahdollisuus yrittää toimintoa uudestaan. 3.7.2 Luokka tipu.db.connectionpool Yhteysvaranto (connection pool) sisältää kaikki käytettävät JDBC-tietokantayhteydet. Servlet pyytää varannosta yhteyden käyttöönsä jokaisen HTTP-pyynnön alussa ja suorittaa kaikki kyseisen pyynnön aikana tehtävät tietokantaoperaatiot annettua yhteyttä pitkin. Yhteysvarantoa käytetään, jotta kaikille käyttäjille voitaisiin taata tasaisesti tietty palvelun taso. Tiettyyn maksimiin rajoitettu yhtäaikaisten tietokantaoperaatioiden määrä takaa seuraavaa: Suorituksessa olevat operaatiot saavat jokainen käyttä vähintään tietyn minimimäärän tietokannan resursseista. Uusille asiakkaille voidaan välittömästi ilmoittaa, mikäli palvelu on niin ruuhkainen, ettei ole kenenkään edun mukaista yrittää suorittaa enää uusia operaatioita juuri kyseisellä hetkellä.

22 Instanssin jäsenmuuttujat Yhteysvarannon jäsenmuuttujiin talletetaan tiedot olemassaolevista yhteyksistä, sekä yhteyden luontiin vaadittavat parametrit. private Set availableconnections Avoimien yhteyksien joukko. Täältä löytyvä java.sql.connection -instanssi tulkitaan kyseisellä hetkellä käyttämättömäksi. private Set busyconnections Käytössä olevien yhteyksien joukko. Yhteysinstanssi siirretään tänne siksi aikaa kun se on varattuna jollekin asiakkaalle. private boolean connectionpending Tämä on true, mikäli uutta yhteyttä ollaan muodostamassa, mutta se ei ole vielä sitoutunut. Kyseessä on pyytäjälle merkki siitä, että uutta yhteyttä ei nyt kannata ryhtyä muodostamaan, sillä uusi on juuri tulossa. private int maxconnections Sallittujen yhtäaikaisten tietokantayhteyksien maksimimäärä. Arvo haetaan asetustiedostosta avaimella JDBC_MAX_CONNECTIONS. private String driver Tietokanta-ajurin luokan täydellinen nimi. Arvo haetaan asetustiedostosta avaimella JDBC_DRIVER.

23 private String username Tietokannan käyttäjänimi. Arvo haetaan asetustiedostosta avaimella JDBC_USERNAME. private String password Tietokannan salasana. Arvo haetaan asetustiedostosta avaimella JDBC_PASSWORD. private String url Tietokannan osoite JDBC:n ymmärtämässä muodossa. Arvo haetaan asetustiedostosta avaimella JDBC_DATABASE. Instanssin rajapinta Tämän luokan instanssi on rajoitettu palvelemaan ainoastaan luokan staattista rajapintaa. Siksi luokan konstruktori on näkyvyydeltään private. Toteutus on kuitenkin instanssiin pohjautuva, sillä luokka vaatii verrattaen laajan alustusoperaation (ks. konstruktori ConnectionPool()), joka on kätevintä toteuttaa juuri konstruktorissa, staattisten alustusmetodien tai -lohkojen sijaan. private ConnectionPool() Konstruktori suorittaa seuraavat alustusoperaatiot: Luodaan joukot (java.util.set) käytetyille ja käyttämättömille yhteyksille. Haetaan tietokannan yhteystiedot luokan tipu.general.tipu- Properties avulla ja instantoidaan JDBC-ajuri (tekniikka dokumentoitu Sunin JDBC 2.0 spesifikaatiossa [6]).

24 Muodostetaan ensimmäinen käyttövalmis yhteys palvelemaan ensimmäistä asiakasta. private Connection getconnection() Tämä metodi hankkii kutsujalleen yhteyden jollakin seuraavista keinoista: Mikäli vapaiden yhteyksien joukosta löytyy toimiva yhteys, otetaan se. Jos yhteys on kuitenkin ollut niin kauan käyttämättömänä, että se on jo ajurin puolesta suljettu, luodaan sen tilalle uusi yhteys, ja yritetään yhteyden varausta uudelleen. Tässä vaiheessa alkuperäinen yhteyden kysyjä kilpailee tasavertaisesti mahdollisesti muidenkin yhteyttä pyytämään tulleiden asiakkaiden kanssa vapaista yhteyksistä. Jos vapaita yhteyksiä ei ole, eikä yhteyksiä ole vielä muodostettu suurinta sallittua määrää, luodaan uusi yhteys ja kilpailutetaan se em. tavalla. Mikäli yhteyksiä ei voida enää luoda, jäädään odottamaan, että jokin käytössä olevista yhteyksistä ilmoittaa vapautuneensa. private Connection makenewconnection() Tämä metodi pyytää java.sql.drivermanager -luokalta uuden yhteyden. Epäonnistunut pyyntö palauttaa arvon null. private void makebackgroundconnection() Tämä metodi käynnistää uuden säikeen muodostamaan yhteyttä. Yhteys muodostetaan omassa säikeessään, koska yhteyden neuvottelu voi kestää useamman sekunnin. Kun se suoritetaan taustalla, voidaan palvella muita yhteyttä pyytämään tai vapauttamaan tulleita asiakkaita rinnalla.

25 Kaikki yhteyttä odottavat asiakkaat herätetään kilpailemaan yhteydestä heti, kun uusi yhteys saadaan käyttöön. private void free(connection con) Tämä metodi vapauttaa tähän yhteysvarantoon kuuluvan yhteyden. Yhteys siirtyy välittömästi vapaiden yhteyksien joukkoon ja mahdollisille odottajille ilmoitetaan tästä. private void closeconnections(set) Tämä metodi sulkee kaikki annetussa yhteysjoukossa olevat yhteydet. Metodia käyttää vain closeallconnections(). private void closeallconnections() Tämä metodi sulkee kaikki avoimet yhteydet, niin käyttämättömät kuin käytössä olevatkin. Metodia kutsutaan ConnectionPool-instanssin tuhoamisen yhteydessä finalize()-metodin kautta. protected void finalize() throws Throwable Javan garbage collection kutsuu tätä metodia, joka puolestaan kutsuu close- AllConnections() -metodia sulkeakseen kaikki auki olevat yhteydet. (Vaihe ei sinänsä ole tarpeellinen sillä yhteydet osaavat myös itse sulkea itsensä siististi kun niiden finalize() -metodia kutsutaan.) Staattiset jäsenmuuttujat private static ConnectionPool instance Koska yhteysvaranto toimii resurssiluokkana, talletetaan sen ainoa instanssi tä-

26 hän muuttujaan. Staattinen rajapinta operoi luokan ominaisuuksilla tämän instanssin kautta. Staattinen rajapinta Tietokantaoperaation suoritukseen tarvittava yhteys pyydetään metodilla requestconnection() ja se täytyy myöhemmin vapauttaa metodilla releaseconnection(). Käyttäjä (servlet) näkee vain nämä kaksi metodia. Connection requestconnection() Tämä metodi pyytää yhteysvarannosta yhteyttä. Suoritus jää tarvittaessa odottamaan, mikäli yhteyttä ei välittömästi saada. void releaseconnection(connection con) Tämä metodi vapauttaa aiemmin varatun yhteyden. Näin tulisi tehdä viimeistään silloin kun kyseisen pyynnön vastaus lähetetään. Yhteydelle voi silti jättää esim. kursoreita auki, sillä itse yhteys säilyy avoimena, vaikka se luovutettaisiinkin toisten operaatioiden käyttöön. private ConnectionPool getinstance() Tämä metodi palauttaa itsenäisen ConnectionPool -instanssin. Metodia kutsuu ainoastaan requestconnection() ensimmäisellä kutsukerrallaan. 3.7.3 Luokka tipu.db.table Table sisältää jonkin tietokannan taulun ns. metadatan, eli taulun nimen, attribuutit sekä jokaiseen attribuuttiin liittyvät tiedot.

27 Servletillä on yksi nimetty kohdetaulu, johon kaikki siltä pyydetyt pyydetyt operaatiot kohdistuvat. Servlet hakee käynnistyessään jokaisen kohdetaulunsa Table -objektin, ja säilyttää niitä jäsenmuuttujissaan elinkaarensa ajan. Vain oikeat tiedot sisältävän Table -instanssin avulla tietokantaoperaatiot voidaan kohdistaa johonkin tiettyyn tauluun, koska tarvittavat SQL-lauseet osataan rakentaa ainoastaan kohdetaulun attribuutti- ja avaintietojen perusteella. Taulujen tiedot luetaan tekstitiedostosta etc/tabledata, jonka jokainen rivi sisältää yhden taulun tiedot seuraavassa muodossa: taulu;attribuutti:avain:null:kommentti;attribuutti:avain:... Taulun nimen (taulu) ja eri attribuuttien erottimena on puolipiste ja attribuutin tietokenttien erottimena kaksoispiste. Tietokentät ovat seuraavat: Attribuutti attribuutti avain null kommentti Attribuutin nimi Attribuutin (taulun sarakkeen) nimi. Y, mikäli tämä attribuutti on taulun pääavain, muutoin N Y, mikäli attribuutti voi saada arvon NULL, muutoin N Attribuutille annettu kommentti Instanssin jäsenmuuttujat Taulun tiedot talletetaan seuraaviin jäsenmuuttujiin: private String tablename Taulun nimi. private SortedSet attributes Taulun attribuuttien nimet (String) aakkosjärjestettynä joukkona.

28 private Map field Kartta taulun attribuuttien tiedoista attribuuttien nimien (String) osoittamina luokan Table.FieldInfo instansseina. Instanssin rajapinta Luokan Table instanssi tarjoaa käyttäjälleen metodit attribuutti tietojen hakemiseen. Attribuutit palautetaan järjestetyssä joukossa, jotta tietokantaoperaatioissa osattaisiin myöhemmin sijoittaa parametrit valmisteltuihin lausekkeisiin (java.sql.preparedstatement) oikeille paikoille. private Table(String tablename, SortedSet attributes, Map fields) Konstruktori sijoittaa parametreina saamansa arvot ja arvojoukot jäsenmuuttujiinsa. attributes sisältää jokaisen ko. taulun sarakkeen nimen merkkijonona (String). fields sisältää jokaisesta sarakkeesta muodostetun Table.FieldInfo -objektin, joka sisältää kyseisen sarakkeen tiedot. String getname() Tämä metodi palauttaa kohteena olevan taulun nimen. SortedSet getattributes() Tämä metodi palauttaa taulun attribuuttien (eli sarakkeiden) nimet joukkona String -objekteja. Joukko on määrittelynsä (ks. [5] luokka java.util.sortedset) mukaisesti aina samassa järjestyksessä. Käytännössä tämä on String -objekteilla nouseva aakkosjärjestys. Huom: Tämä ei ole attribuuttien taulumäärittelyn mukainen järjestys, vaan ainoastaan se järjestys, jossa attribuutit tullaan sijoittamaan tietokantaoperaa-

29 tiossa luotavaan SQL-lauseeseen. Käytännössä SortedSet järjestää merkkijonot nousevaan aakkosjärjestykseen. SortedSet getkeys() Tämä metodi palauttaa taulun attribuuteista ne, jotka ovat taulun pääavaimia. Usein avaimia on vain yksi, mutta niitä saattaa olla myös kaksi tai enemmän. Mikäli joukossa on useita attribuutteja, ne ovat joukon sisällä järjestettynä samoin kuin kaikki attribuutit käsittävä joukko. Huom: Tämä ei ole avainten taulumäärittelyn mukainen järjestys, vaan ainoastaan se järjestys, jossa avaimet tullaan sijoittamaan tietokantaoperaatiossa luotavaan SQL-lauseeseen. Käytännössä SortedSet järjestää merkkijonot nousevaan aakkosjärjestykseen. int gettype(string attribute) Tämä metodi palauttaa pyydetyn attribuutin tyypin java.sql.types -luokan määrittelemänä vakiona. String getcomment(string attribute) Tämä metodi palauttaa pyydetyn attribuutin kommentin, mikäli sellainen on tiedossa. Muutoin palautetaan tyhjä merkkijono. boolean iskey(string attribute) Tämä metodi palauttaa arvon true, mikäli kysytty attribuutti on taulun pääavain. Muutoin palautetaan false. boolean isnullable(string attribute) Tämä metodi palauttaa arvon true, mikäli kysytty attribuutti voi saada arvon

30 NULL. Muutoin palautetaan false. Stattiset jäsenmuuttujat Taulut, ja niiden lukemiseen tarvittavat vakiot talletetaan seuraaviin staattisiin jäsenmuuttujiin: private static String ATTRIBUTE_SEPARATOR Taulun kuvaavan rivin taulun nimikentän, ja attribuuttien tietokenttien erotin. Oletusarvoisesti puolipiste ( ; ). private static String FIELD_SEPARATOR Yhden attribuutin tietokenttien erotin. Oletusarvoisesti kaksoispiste ( : ). private static Map tabledata Kartta taulujen nimistä (String) Table -luokan instansseihin. Staattinen rajapinta Luokan Table instansseja pyydetään staattisen rajapinnan avulla. Table gettable(string tablename) Tämä metodi hakee tabledata -kartasta parametriaan tablename vastaavan taulun. Taulujen nimet käsitellään ja tarvittaessa muunnetaan aina isoiksi kirjaimiksi. Mikäli karttaa tabledata ei vielä ole alustettu, se tehdään ennen taulun hakua metodilla loadtabledata.

31 private static Map loadtabledata() Tämä metodi lukee resurssitiedostosta etc/tabledata kaikkien kannassa käytettyjen taulujen kuvaukset. Luku tapahtuu selaamalla kyseistä tiedostoa rivi riviltä, luomalla jokaista riviä vastaavan Table -instanssin tabledata -karttaan. private static FieldInfo parsefield(stringtokenizer field) Tämä metodi on loadtabledata:n apumetodi, joka muodostaa yhden taulun attribuutin kuvauksen annetusta pilkotusta merkkijonosta. 3.7.4 Sisäluokka Table.FieldInfo Tämä luokka on luokan Table staattinen sisäluokka, ja se toimii yksinkertaisena taulun attribuutin kuvaavana tietorakenteena. Jäsenmuuttujat Yhden attribuutin tiedot talletetaan seuraavasti: int type Attribuutin tyyppi (ks. java.sql.types) boolean key Onko attribuutti pääavain? boolean nullable Voiko attribuutti olla NULL?

32 String comment Attribuutin kommentti. Instanssin rajapinta Luokka ei sisällä metodeja. 3.7.5 Luokka tipu.db.databaseoperation DatabaseOperation on abstrakti yleistys kaikista mahdollisista servletien tietokantaan kohdistamista operaatioista. Jokaisella operaatiolla (poislukien SearchOperation) oletetaan olevan yksi tiettyyn tauluun kohdistuva valmisteltu lauseke (java.sql.prepared- Statement), jollaisen instanssia DatabaseOperation säilyttää jokaista ko. operaation suoritukseen tarjottua tietokantayhteyttä kohti. Näin täytyy tehdä, sillä valmistellut lausekkeet ovat sidottuja siihen yhteyteen, jolla valmistelu on alkujaan suoritettu. Instanssin jäsenmuuttujat Jokaiselle tietokantaoperaatiolle yhteisiä ovat seruaavat tiedot: protected OperationResults result Operaation tulos talletetaan tähän. Toteuttava tietokantaoperaatio voi missä tahansa vaiheessa tämän muuttujan avulla asettaa operaation tilan epäonnistuneeksi, jolloin se raportoidaan käyttäjälle. protected Table table Operaation kohdetaulun instanssi.

33 private Map statementmap Tämä kartta sisältää jokaiselle käytössä olevalle tietokantayhteydelle (java.sql.connection) käännetyt SQL-lauseet (java.sql.prepared- Statement). Instanssin rajapinta Jokainen tietokantaoperaatio on oltava valmisteltavissa siten, ettei joka kerta kyseistä operaatiota tiettyyn tauluun kohdistettaessa tarvitse kyselylauseketta kääntää uudestaan. Siksi jokaisen tietokantaoperaation on toteutettava tietty tietokantaoperaatioiden hallinnalle yhteinen rajapinta. DatabaseOperation(Table table) Konstruktori luo geneerisen tietokantaoperaation, joka kohdistuu tauluun table. PreparedStatement getpreparedstatement(connection) Tämä metodi palauttaa annetulle yhteydelle valmistellun tätä operaatiota vastaavan lausekkeen. Mikäli valmistelua ei juuri tälle yhteydelle vielä ole tehty, se tehdään metodia kutsuttaessa, ja talletetaan tämän operaation instanssiin käytettäväksi juuri tälle yhteydelle tämän operaation tulevia suorituksia varten. int getresultsettype() Tämä metodi palauttaa operaation vaatiman/salliman vastausjoukon kursorityypin, jonka tulee olla on yksi java.sql.resultset luokan arvoista TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE tai TYPE_SCROLL_SENSITIVE. Oletuksena palautetaan arvo TYPE_FORWARD_ONLY.

34 int getresultsetconcurrency() Tämä metodi palauttaa operaation vaatiman/salliman tietueiden eristyneisyysasteen, jonka tulee olla toinen java.sql.resultset luokan arvoista CONCUR_READ_ONLY tai CONCUR_UPDATABLE. Oletuksena palautetaan arvo CONCUR_READ_ONLY. Abstrakti rajapinta Ainoa asia mitä geneerinen tietokantaoperaatio ei voi määritellä on SQL-lause, joka määrää mitä kyseinen operaatio käytännössä tekee. Siksi jokaisen erikoistuneen operaation on toteutettava luokan DatabaseOperation abstrakti metodi getsqlstring(). String getsqlstring() Palauttaa toteuttavan operaation SQL-kielisen lausekkeen, johon on objektia luotaessa annetun kohdetaulun avainjoukon avulla luotu valmis parametrilista. 3.7.6 Luokka OperationResults Jokainen tietokantaoperaatio päättyy tulokseen, joka on luettavissa operaation execute-metodin palauttamasta OperationResults -tyyppisestä objektista. Perustoteutus tarjoaa rajapinnan totuusarvoisen tuloksen ja virheilmoituksen lukemiseen. Monimutkaisempaa tulosten raportointia varten voidaan tästä luokasta periyttää uusi tulosobjekti. Näin on tehty esim. hakutulosten kanssa (ks. 3.7.8). Instanssin rajapinta Koska jokainen operaatio voi vähintäänkin onnistua tai epäonnistua,

35 tarjoaa jokainen tulosobjekti metodit tämän tilan selvittämiseen. Lisäksi tarjotaan metodit tilan asettamiseen. Näitä metodeja käyttävät vain tipu.db.databaseoperation -luokasta periytyvät luokat, joten niiden näkyvyys on package private (eli ei merkittyä määrettä). OperationResults() Luo OperationResults() -objektin, jossa virhettä ei ole tapahtunut. OperationResults(String message) Luo OperationResults() -objektin, jossa on tapahtunut virhe, ja asettaa annetun virheilmoituksen. boolean succeeded() Tämän metodin avulla palvelinsovelma voi nopeasti päätellä ryhdytäänkö virheenkäsittelyoperaatioihin, vai jatketaanko normaalin vastauksen tuottamisella. Metodi palauttaa siis true, mikäli virheitä ei tapahtunut. String geterrormessage() Mikäli suoritettu operaatio epäonnistui (succeeded() palauttaa epätoden), tällä metodilla voidaan kysyä käyttäjälle ilmoitettava virheilmoitus. Sovelma ei tämän tekstin perusteella voi suorittaa mitään korjaavia toimenpiteitä. Teksti on tarkoitettu ainoastaan virheen raportointiin. Mikäli virheilmoitusta ei ole tietokantaoperaatiossa annettu, on tämän metodin tuloste määrittelemätön. void seterrormessage(string message) Tällä metodilla asetetaan tuloksen virheilmoitus.

36 void seterror(string message) Tällä metodilla asetetaan tuloksen tila virheelliseksi (succeeded() palauttaa false), ja asetetaan samalla virheilmoitus käyttäjän luettavaksi. 3.7.7 Luokka tipu.db.searchoperation SearchOperation suorittaa SELECT-lauseella haun tietokannan tauluun annetuilla attribuuttimaskeilla. Kaikki vertailut tehdään LIKE-operaattorein, joten käyttäjät voivat itse syöttää jokerimerkkejä hakukenttiin. Tulokset palautetaan erillisessä SearchResults-objektissa. Instanssin rajapinta Hakuoperaation rajapinta on pääosin sama kuin muidenkin tietokantaoperaatioiden. Haku suoritetaan metodilla executesearch() antamalla parametriksi kartta hakuarvoista. Tuloksena käyttäjä saa erillisen tulosobjektin SearchResults. SearchOperation(Table table) Konstruktori luo uuden, tauluun table kohdistuvan hakuoperaation. OperationResults executesearch(connection con, Map values) Tämä metodi suorittaa hakuoperaation kohdetauluun annetulla yhteydellä con hakuarvoilla values. Kutsu palauttaa haun tuloksena SearchResults -instanssin (ks. 3.7.8).

37 OperationResults executesearch(connection con, Map values, String sortkey) Tämä metodi suorittaa hakuoperaation kohdetauluun annetulla yhteydellä con hakuarvoilla values. Tulokset järjestetään kohdetaulun sarakkeen sortkey mukaan. Kutsu palauttaa haun tuloksena SearchResults -instanssin (ks. 3.7.8). protected String getsqlstring(map values, String sort- Key) Tämä metodi palauttaa hakulausekkeen muotoa: SELECT ROWID FROM taulu WHERE attr1 LIKE value1 AND attr2 LIKE value2... AND attrn LIKE valuen ORDER BY sortkey Kentät attr? ovat kohdetaulun attribuutteja (parametrin values avaimia). Kentät value? ovat hakuarvoja (parametrin values annettua avainta vastaavia arvoja). ORDER BY -lause lisätään vain, mikäli sortkey null. String escapequotes(string s) Tämä metodi palauttaa parametrinaan saamansa merkkijonon SQL-lauseeseen sopivassa muodossa, jossa mahdolliset heittomerkit on kahdennettu, jotta niitä ei tulkittaisi merkkijonoskalaarin erottimiksi.

38 3.7.8 Luokka tipu.db.searchresults Luokka SearchResults periytyy luokasta OperationResults. Lisäksi se sisältää listan haussa löytyneiden kohdetaulun rivien ROWID:t, joita voidaan selata vapaassa järjestyksessä. Rivien varsinaiset tiedot haetaan vain pyydettäessä, eikä kursoreita pidetä suotta auki. Haun suorittava servlet säilyttää kullekin sessiolle viimeisintä hakutulosta ja lukee sieltä arvoja aina kun käyttäjä liikkuu tuloslistassa eteen- tai taaksepäin. Instanssin jäsenmuuttujat Hakutulokset talletetaan, ja niitä selataan seuraavilla jäsenmuuttujilla: private int currentrow Rivilistan senhetkinen selauskohta. private SimpleDateFormat dateformat Muoto, jossa päiväykset on talletettu tietokantaan (pp.kk.vvvv). private Vector rowlist Lista Oraclen ROWID-objekteja kuvaamassa jokaista löydettyä riviä. private Table table Taulu, johon haku kohdistettiin. Instanssin rajapinta Hakuoperaatiota kutsunut servlet pääsee käsiksi hakutuloksiin luokan

39 SearchResults instanssin rajapinnan kautta. Suurin osa seuraavista metodeista heijastaa kursorin toiminnallisuutta, joten hakujoukossa voi liikkua samoin askelin kuin käyttäjä valitsee näytöltä hakutuloksia: seuraava, edellinen tai satunnainen (listasta osoitettu) tietue. Hakuoperaatiolle ominaisten metodien lisäksi SearchResults sisältää myös luokan OperationResults määrittelemät yleiset operaation tilaa käsittelevät toiminnot. protected SearchResults(ResultSet rs, Table t) Konstruktori luo instanssin, joka käsittelee annettua vastausjoukkoa. private void setresultset(resultset rs) Parametri rs sisältää löydettyjen rivien ROWID:t, jotka luetaan objektin jäsenmuuttujaan rowlist. Tämän jälkeen vastauskursori suljetaan. SortedMap getcurrentrow() Tämä metodi palauttaa vastausjoukon senhetkisen rivin järjestettynä (attribuutti arvo) karttana. SortedMap getnextrow() Tämä metodi palauttaa järjestyksessä nykyistä kohtaa seuraavan rivin. Kursoria siirretään yksi rivi eteenpäin. Metodi palauttaa arvon null, mikäli kursori on viimeisellä rivillä. SortedMap getpreviousrow() Tämä metodi palauttaa järjestyksessä nykyistä kohtaa edeltävän rivin. Kursoria

40 siirretään yksi rivi taaksepäin. Metodi palauttaa arvon null, mikäli kursori on ensimmäisellä rivillä. SortedMap getabsoluterow(int rownum) Tämä metodi palauttaa käyttäjän määrittelemän absoluuttisen rivin. Rivien numerointi alkaa yhdestä. SortedMap getabsoluterow(string rowid) Tämä metodi palauttaa käyttäjän määrittelemän absoluuttisen rivin. private SortedMap getabsoluterow(rowid rowid) Tämä metodi palauttaa käyttäjän määrittelemän absoluuttisen rivin. Tätä muotoa Kutsutaan ainoastaan SearchResult:in sisällä. Metodi suorittaa haun ko. rowid:llä kohdetauluun ja palauttaa saadut attribuutit SortedMap:ina. List getrowcount() Tämä metodi palauttaa haussa löytyneiden rivien määrän. int getrownumber() Tämä metodi palauttaa sen rivin numeron, jolla kursori tällä hetkellä on. Arvo on väliltä, missä on tulosjoukon rivien määrä. removerow(rowid rowid) Poistaa kyseisen rivin hakulistasta. Tätä kutsutaan servletistä käsin, jotta juuri poistettu tietue ei näkyisi vastausjoukossa.

41 removerow(string rowid) Poistaa kyseisen rivin hakulistasta. Tätä kutsutaan servletistä käsin, jotta juuri poistettu tietue ei näkyisi vastausjoukossa. 3.7.9 Luokka tipu.db.insertoperation Lisäysoperaatiolla voidaan lisätä yksi rivi määrättyyn tietokannan tauluun. Lisäys onnistuu, mikäli taulussa ei vielä ole samoilla avaimilla varustettua tietuetta, taulun eheys- ja avainrajoitteet toteutuvat eikä tiedonsiirtovirheitä tapahdu. Tulokset palautetaan OperationResults -objektina. Instanssin rajapinta Keskustelu lisäysoperaation (kuten muidenkin operaatioiden) kanssa on hyvin suoraviivaista. Ensin operaatio konstruoidaan antamalla parametriksi kohdetaulu. Tämän jälkeen koostetaan arvot kartaksi ja kutsutaan niillä metodia executeinsert(), joka suorittaa itse operaation. InsertOperation(Table table) Konstruktori luo uuden, tauluun table kohdistuvan lisäysoperaation. OperationResults executeinsert(connection con, Map values) Tämä metodi lisää rivin kohdetauluun arvoilla values. Ko. arvokartasta tulee löytyä arvot ainakin kaikille pääavaimille sekä muille NOT NULL -attribuuteille.