Tässä ohjeessa käsitellään vain servletteihin liittyviä erityispiirteitä. Harjoitustöissä voidaan käyttää joko koneessa kontti.helsinki.fi toimivaa Oracle 8.0.5-tietokantaa, koneessa bodbacka.cs.helsinki.fi (ehkä jatkossa db.helsinki.fi) toimivaa Oracle 9.1 -tietokantaa tai koneessa db.cs.helsinki.fi käyttäjän omaa PostgreSQL- tai MySQL kantaa.
- Oracle tietokannan käyttöön liittyviä yleisiä ohjeita löytyy ohjeesta Oracle Web Server:iä käyttävien WWW-sovellusten toteutus Tietokantasovellusten harjoitustyö -kurssilla . Tarjolla on myös Bodbacka-tietokantaan liittyviä ohjeita". Oracle käyttöä varten on pyydettävä ohjaajalta käyttäjätunnus. Koneessa bodbacka.cs.helsinki.fi olevan kannan käyttöön tarvittavia asetuksia alla:
export CLASSPATH=/home/tkt_orac/OraHome1/JRE/lib:/home/tkt_orac/JRE/lib/rt.jar export ORACLE_HOME=/home/tkt_orac/OraHome1 export LD_LIBRARY_PATH=/home/tkt_orac/OraHome1/lib:$LD_LIBRARY_PATH export TWO_TASK=test export PATH=$PATH:/home/tkt_orac/OraHome1/bin export NLS_LANG=finnish_finland.we8iso8859p1Laita ylläolevat vaikka .bashrc tiedostoosi ko. koneella, niin saat ainakin käynnistettyä SQL-tulkin (sqlplus). Toistaiseksi tulkin käynnistyskomento täytyy antaa muodossa sqlplus tunnus/salasana@test, missä test on tietokantainstanssin nimi. Tietokantahallintajärjestelmän suorakäyttöä varten täytyy tällä hetkellä kirjoittautua palvelinkoneelle. Yritämme etsiä koneelle, jonkin sqlplus:aa näppärämmän kysely-ympäristön.
- PostgreSQL ohjeita löytyy manuaaleista:
PostgreSQL-kannan voi pystyttää koneesta db.cs.helsinki.fi löytyvillä skripteillä:
- PostgreSQL Administrator's Manual
- PostgreSQL User's Manual
- PostgreSQL Programmer's Manual
- PostgreSQL Tutorial
PostgreSQL starttiskripti nostaa kannan toimintaan vain rajatuksi aikaa (10h). Kun työ jätetään tarkastukseen on hakemistossa /usr/local/bin olevasta skriptistä syytä tehdä paikallinen kopio (eri nimellä) ja editoida sitä (time asetus) niin, että kanta pysyy pystyssä ohjaajan kanssa sovitun ajan sekä suorittaa käynnistys tällä muutetulla skriptillä.
- kannan perustus skriptillä wanna-postgres
- postgres ympäristön asetus setup postgres kun kanta on ensin perustettu ja halutaan käyttää komentotulkkia pgsql.
- kannan käynnistys skriptillä start-postgres
- kannan alasasajo skriptillä stop-postgres
- Myös MySQL-kannan vastaavat skriptit löytyvät koneesta db.cs.helsinki.fi.
Hyviä johdatusartikkeleita ovat esimerkiksi:
Servletti muodostuu yhdestä pääluokasta ja mahdollisista apuluokista. HTML-sivulta servlettiä kutsutaan oletusarvoisesti pääluokan nimellä (servletille voi konfigurointitiedostossa antaa jonkin muunkin nimen). Servlettien ajoympäristönä toimii www-palvelinohjelmistoon kytketty servlettimoottori (servlet engine), joka pitää sisällään Java-ajoympäristön. HY/TKTL:n tapauksessa kyseessä on Apache www-palvelimeen on kytketty jserv servlettimoottori. Servletti ladataan ajoympäristöön kun sitä ensimmäisen kerran kutsutaan.TKTL:llä servlettejä voi ajaa vain koneessa db.cs.helsinki.fi. Tällä koneella servlettejä haluava luo itselleen servlettipuitteet (= hakemiston, jonne servletit sijoitetaan ja ajoympäristön käynnistys- ja alasajoskriptit) ajamalla skriptin
wanna-servlet
Puitteet luovat ajoympäristön käynnistystä varten konfigurointitiedostot alihakemistoon jserv/etc. Ajoympäristön keskeiset konfigurointitiedot, mm javan CLASSPATH määrittely ja ympäristön elinaika, ovat tiedostossa environment. Tiedostossa on kommentoituna käskyt Oracle tai PostgreSQL -tietokanta-ajurin liittämiseksi CLASSPATH:iin. Jos haluat ottaa ajurin käyttöön käy editoimassa kommenttimerkki (#) pois käskyn edestä. CLASSPATH:iin ei ole syytä sisällyttää tarpeettomia ajureita, sillä ajurikirjastot sisältävät samannimisiä luokkia ja usean kirjaston mukanaolo edellyttää tarkenteita luokkaviitauksessa. Jos haluat CLASSPATH:iin muita muutoksia kirjaa ne myös tähän tiedostoon. Jos haluat jättää ajoympäristön käyntiin pidemmäksi kuin oletusajaksi (esim. työn tarkastusta varten) on lifetime muuttujan arvoa muutettava.Servlettiympäristö käynnistetään komennolla start-servlet Käynnistysskripti pitää ajoympäristön pystyssä oletusarvoisesti 10 tuntia. Jos haluat ympäristön alas ennen sitä, voit tehdä alasajon skriptillä stop-servlet. Katso ylläpidon servlet-ohje". Ajoympäristö on ajettava alas esimerkiksi, kun jotain servlettien käyttämää apuluokkaa, tai CLASSPATH:ia on muutettu ja muutokset halutaan näkyviin. Servlettiluokan itsensä muuttuessa servlettiajuri osaa ladata uuden koodin automattisesti.
Ajoympäristö luo serveletin pääluokasta (servlettiluokasta) lataamisen yhteydessä yhden ilmentymän (olion). Tämä on ainoa ilmentymä, joka servlettiluokasta luodaan. Olio säilyy hengissä kunnes ajoympäristö suljetaan tai servlettiluokan koodia lataushakemistossa muutetaan. Jotta ajoympäristö osaisi ladata servletit, on servlettien class-tiedostot sijoitettava ympäristön konfiguroinnin yhteydessä määriteltyyn paikkaan, lataushakemistoon. Puitteiden luontiskripti määrittelee lataushakemiston käyttäjän alihakemistoksi jserv/servlets. Servlettiin viitataan url:lla http://db.cs.helsinki.fi/s/username/servlet_class_name. Esimerkiksi http://db.cs.helsinki.fi/s/laine/PgTesti käynnistää koneen db.cs.helsinki.fi hakemistoon /home/laine/jserv/servlets/ sijoitetun luokan PgTesti.class koodia suorittavan servletin käyttäjän laine servlettiympäristössä.
Servlettien kääntämiseen tarvittavat CLASSPATH muunnokset saa tehtyä skriptillä setup servlet, joka käyttää asetuksiin hakemistossa jserv/etc olevaa environment tiedostoa.
Servletin palvelut toteutetaan servlettiluokan palvelumetodien doPost tai doGet avulla. Saadessaan servlettiin kohdistuvan palvelupyynnön servlettiajuri käynnistää palvelumetodin erillisenä rinnakkaisena säikeenä. Se kumpi metodeista goGet tai doPost käynnistetään riippuu kutsusta. Jos kutsu annetaan HTML-linkin kautta tai html-lomakkeen method-attribuutin arvona on 'get', käynnistetään doGet-palvelu ja muutoin doPost -palvelu. Kumpikin näistä metodeista on syytä toteuttaa saman sisältöisenä, eli varsinainen koodi kirjoitetaan vain toiseen näistä ja toinen määritellään kutsumaan sitä.
Palvelinmetodit pitää koodata säieturvallisiksi, eli niitä on kyettävä ajamaan rinnakkain ilman, että ne sotkevat toistensa tietoja. Yksinkertaisimmin tämän saa aikaan käyttämällä metodeissa vain metodin sisäisiä paikallisia muuttujia ja olioita sekä hoitamalla tarvittava tilatiedon välitys eri servlettien välillä www-sivujen ja selaimen kautta, esimerkiksi lomakkeen piilokenttinä tai pipareina (cookie).
Servlettien rajapinta on määritelty javax.servlet- ja javax.servlet.http-pakkauksissa, jotka on otettava mukaan ohjelmaan import:lla. Pakkaukset sisältyvät JSDK (jsdk.jar) kirjastoon, joka pitää liittää CLASSPATH:iin. Kirjastosta on eri versioita, viimeisin on versio 2.3. Tässä ohjeesa esitettävät asiat toimivat myös vanhempien versioiden yhteydessä. Laitoksen nykyinen jserv-servelettiympäristö on JSDK versio 2.0 yhteensopiva. Polku ajuriin on määritelty tiedostossa ../jserv/etc/environment. Tämän ajurin dokumentaatiota ei enää löydy, joten lähinnä lienee version 2.1 dokumentti.Kaikkien servlettiluokkien (servletin pääluokkien) tulee periä luokka HttpServlet. Tätä kautta periytyvät palvelumetodien doGet, doPost ja service hahmot, esimerkiksi
public void doPost( HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { }Kaikilla palvelumetodeista on kaksi parametria req ja resp. Req-parametrina annettava HttpServletRequest luokan olio tarjoaa palvelut servletin kutsun yhteydessä annettujen parametriarvojen käsittelyyn. Resp-olio hoitaa servletin tulokset takaisin www-selaimelle.
Palvelumetodin alkutoimiin kuuluvat
- tulosten käsittelyyn liittyvät alkutoimet,
- palvelupyynnön yhteydessä annettujen parametrien purkaminen ja
- mahdollisesti tietokantayhteyden muodostus
Tulosten käsittelyyn liittyvät alkutoimet
Ensimmäisenä alkutoimenpiteenä suoritetaan yleensä tulosten käsittelyyn liittyvät alkutoimet:Seuraavassa yksi tapa tehdä nämä alustukset:
- tuloksen tyypin määrittely ja
- kirjoitinolion kytkentä tulostusta hoitamaan.
resp.setContentType("text/html"); PrintWriter pw = res.getWriter();Näiden lisäksi voidaan alkuvaiheessa tulostaa www-sivun otsaketiedot. Jos usea servletti tuottaa samat otsaketiedot kannattaa määritellä joko luokkahierarkiassa HttpServletin ja varsinaisen servlettiluokan väliin tai luokkahierarkiasta erilleen apuluokka hoitamaan otsakkeet.Palvelupyynnön yhteydessä annettujen parametrien purkaminen
Servletin kutsuparametrit annetaan HTML:ssä samalla tavoin kuin CGI-ohjelmia kutsuttaessa. Ohjelma saa parametrien arvot käyttöönsä palvelumetodin req-parametrin kautta. Parametrin arvoa pyydetään req-oliolta parametrin nimelläString arvo = req.getParameter("parametrin_nimi");Pyyntö tuottaa merkkijonomuotoisen arvon, joka tarvittaessa on erikseen munnettava vaikkapa kokonaisluvuksi. Ellei annetun nimistä parametria löydy on tuloksena arvo null. Moniarvoisten parametrien käsittelyyn on oma metodi getParameterValues, joka tuottaa tuloksenaan merkkijonotaulukon.Tietokantayhteyden avaus
Jos kyseessä on tietokantaa käyttävä servletti liittyy alkutoimiin myös tietokantayhteyden muodostus. Jos käytössä on yhteysallas, jonne varastoidaan auki pidettäviä tietokantayhteyksiä, voidaan yhteys luoda varaamalla yhteys yhteysaltaasta. Harjoitustöissä, kun suoritusnopeus ei ole tärkeää, voidaan jokaisen palvelumetodin alussa perustaa uusi yhteys.Yhteyden luomiseksi on ladattava tietokantakohtainen ajuri ja otettava se käyttöön sekä luotava sen avulla yhteys. Seuraava funktio createConnection palauttaa uuden tietokantayhteyden. Jos sovelluksessa on useita tietokantaa käyttäviä servlettejä, kannattaa HttpServlet-luokan ja servlettiluokkien väliin yleistyshierarkiaan luoda vaikkapa luokka HttpDataBaseServlet, joka tarjoaa tietokantayhteyden muodostus- ja sulkupalvelut.
public Connection createConnection( String driverName, String dataBase, String user, String password) { try { Class.forName(driverName); } catch (ClassNotFoundException ex) return null; } Connection conn= null; try { conn= DriverManager.getConnection(dataBase, user, password); } catch (SqlException sex) { conn=null; } return conn; }Yhteys koneella bodbacka.cs.helsinki.fi olevaan Oracle 9 -kantaan test saadaan aikaan seuraavasti:conn= createConnection("oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@bodbacka.cs.helsinki.fi:1521:test", user,password);Ajurin löytämiseksi on CLASPATH:sta löydyttävä/opt/jdbc/oracle/classes12.zipKun yhteys halutaan palvelimella kontti olevaan Oracle 8 -kantaan tktb, on kutsu seuraava:conn = createConnection("oracle.jdbc.driver.OracleDriver", "jdbc:oracle:thin:@kontti.helsinki.fi:1522:tktb", user,password);Tällöin CLASSPATH:ssa on oltava/opt/jdbc/classes12_01.zipKoneella db.cs.helsinki.fi olevaan kayttäjän username postgeSQL-kantaan saadaan yhteys kutsulla:conn = createConnection("org.postgresql.Driver", "jdbc:postgresql://localhost:portnumber/username", username,password);Tässä portnumber on asennusskriptin antama porttinumero ja username kayttäjän unix-käyttäjätunnus.Tietokanta-ajuriin osoittava polku on liitettävä CLASSPATH:iin. Ajurit Oraclelle ja PostgreSQL:lle löytyvät hakemistosta /opt/jdbc. Oracle 8.1-thin ajuri JDK 1.2:lle on nimeltään classes12_01.zip ja siihen liittyvä kansallismerkistöpakkaus nls_charset12_01.zip. Oracle 9.1-thin ajuri JDK 1.2:lle on nimeltään classes12.zip tai classes12.jar. PostgreSQL ajuri on nimeltään jdbc7.0-1.2.jar.
Rungon toiminnallisuus riippuu siitä, mitä palvelinmetodin on tarkoitus tehdä. Yleensä palvelinmetodi ainakin tuottaa html-muotoisen tulossivun. Sivulle kirjoitetaan tekstiä käyttämällä alustustoimien yhteydessä res-olioon kytkettyä kirjoitinoliota (yllä PrintWriter pw).Wenla-projekti on tuottanut kirjasto-olioita www-sivujen tuottamisen helpottamiseksi. Kirjastoa ei ole juurikaan kokeiltu käytännössä, mutta sieltä voi ainakin katsoa esimerkkejä.
Tietokantaoperaatiot
Tietokantaoperaatioita varten luodaan tyypillisesti Statement- tai PreparedStatement-olio. CallableStatement oliota voi käyttää tietokantaproseduurien ja funktioiden käynnistykseen. PreparedStetement ja CallableStatement perivät Statement luokan. Jatkossa kaikkien luokkien olioita kutsutaan Statement-olioiksi. Tietokantaoperaatioihin liittyvät Statement-oliot luodaan tietokantayhteysolion (Connection -olio) avulla. Ne on aina suljettava ohjelman lopuksi. Lähes kaikki tietokantaoperaatiot voivat aiheuttaa SQLException poikkeuksen, joka on pyydystettävä ohjelmassa. Tietokantaoperaatioita ei ole välttämätöntä upottaa suoraan palvelumetodien koodiin. Usein saattaa olla kätevämpää käyttää jotain apuluokkaa. Seuraavassa esimerkki tietokantasilmukasta:Statement stm=null; ResultSet rs=null; String kysely= "select nimi from opiskelija where hetu like \'0101%\' order by nimi"; // tammikuun 1. päivänä syntyneet try { stm= conn.createStatement(); rs= stm.executeQuery(kysely); while (rs.next()) { pw.println(rs.getString("nimi")+"<br>"); } } catch (SQLException e) { databaseError(e.getErrorCode(), e.getMessage(), pw); // databaseError on tässä itse tehty virheilmoitusmetodi } finally { // finally takaa, että tietokantaoperaatioihin liittyvä // close suoritetaan myös virhetilanteessa // Jos try osasta tullaan pois return lauseella tätä ei suoriteta // moista ei siis pidä tehdä try { if (rs!=null) rs.close(); if (stm!=null) stm.close(); } catch (SQLException ee) {} // kursorin sulkemisen ei }
Palvelumetodin lopuksi on vapautettava tietokantaresurssit ja päätettävä HTML-sivu. Tietokantaresurssien vapauttamiseen sisältyy kaikkien ResultSet ja Statement (ja sen aliluokkien) olioiden close-metodien kutsuminen. JDBC-manuaalissa todetaan, 'ettei tämä ole yleensä välttämätöntä vaan java-roskienkerääjä hoitaa asian joskin jotkut tietokantapalvelimet vaativat, että resurssit vapautetaan eksplisiittisesti close metodeja kutsumalla'. Ainakin Oraclen kohdalla tilanne on tällainen ja close täytyy suorittaa jokaisessa ohjelman päättymishaarassa. Jos se jätetään suorittamatta resusseja jää palvelinpäässä auki ja koko palvelin saattaa hyytyä. Myös tietokantayhteys pitää sulkea lopuksi, esim erillisellä metodillapublic void endConnection(Connection conn) { try { conn.close(); catch (SQLException e) {} }Esimerkki
Esimerkki servletistä