Servletit ovat sovelluspalvelimessa ajettavia Java-ohjelmia, jotka voidaan käynnistää webbipalvelimen kautta.
Servleteistä löytyy runsaasti informaatiota esimerkiksi sivulla olevan linkkikokoelman kautta.Hyviä johdatusartikkeleita ovat esimerkiksi:
- Stefan Zeiger : Servlet Essentials
- Hans Bergsten: An Introduction to Java Servlets
- Mark Andrews: STORY OF A SERVLET: AN INSTANT TUTORIAL
- MageLang Institute: Fundamentals of JavaTM Servlets Preface
- Dynamic Information Systems Corporation: Developing JDBC Servlet Applications
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. Servletti ladataan ajoympäristöön kun sitä ensimmäisen kerran kutsutaan. Latausta varten servlettiluokat, apuluokat, kirjastot ja sovelluksen www-sivut on asennettava tiettyihin standardin määrittelemiin hakemistoihin.
HY/TKTL:llä servlettejä voi ajaa vain koneessa db.cs.helsinki.fi. Ajoympäristönä on joko Tomcat 4.1.10-palvelin tai jserv-palvelin.Servlettien asennus Tomcat ympäristöön
Servlettikäyttöön tarvittavan hakemistorakenteen ja servlettiympäriston käynnistys- ja sammutusskriptit saat käyttöösi komennolla wanna-tomcat.
Olkoon SERVLET servlettien päähakemisto. Käyttöönottoskripti (wanna-tomcat) täsmentää mikä tämä hakemisto tarkalleen on (todennäköisesti ~user/tomcat/webapps/). Kun sovelluksen komponentit asennetaan alla kuvattavalla tavalla osaa Tomcat muokata CLASSPATH ympäristömuuttujan ja ladata servlettiluokat sekä näyttää sovelluksen staatiset sivut.
- Hakemistoon SERVLET sijoitetaan sovelluksen staattiset HTML-sivut, tyylitiedostot, kuvat, JavaScript-tiedostot, jne.
- Tiedosto SERVLET/WEB-INF/web.xml on Web-sovelluksen konfigurointitiedosto. Tässä tiedostossa voidaan esimerkiksi määritellä alustusparametreja ja uudelleennimetä servlettejä.
- Hakemistoon SERVLET/WEB-INF/classes/ sijoitetaan servlettiluokat ja sovelluksen apuluokat sekä hakemiston alihakemistoiksi käytettävät omat pakkaukset. Esimerkiksi pakkauksen 'box' luokat sijoitetaan hakemistoon SERVLET/WEB-INF/classes/box/ .
- Hakemistoon SERVLET/WEB-INF/lib/ sijoitetaan ne sovelluksen käyttämät kirjastot, joita ei ole asennettu yleisesti tarjolle kaikille sovelluksille. Tänne sijoitetaan esimerkiksi tietokanta-ajurin kirjasto. Yleisesti tarjolla olevia kirjastoja ovat servletti-API ja muut servlettiympäristön mukana tulevat kirjastot. Nämä on asennettu hakemistoon $CATALINA_HOME/common/lib/ tai $CATALINA_HOME/shared/lib/ (jos näissä olevia luokkia käytetään on hakemistojen oltava CLASSPATH polulla käännösvaiheessa).
Servlettien asennus Jserv-ympäristöön
Servlettikäyttöön tarvittavan hakemistorakenteen ja servlettiympäristön käynnistys- ja sammutusskriptit saat käyttöösi komennolla
wanna-servlet.Skripti luo sinulle alihakemiston jserv ja tälle alihakemistot etc, servlets ja logs. Ajoympäristön keskeiset konfigurointitiedot, mm Javan CLASSPATH määrittely ja ympäristön elinaika, ovat tiedostossa [user_login]/jserv/etc/environment. Tiedostossa on kommentoituna käskyt Oracle8 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.
HUOM:Bodbackan Oracle 9 -kannan käyttämiseksi environment tiedostossa oleva Oracle-ajuri pitää korvata ajurilla /opt/jdbc/oracle/classes12.zip .
Servlettien *.class tiedostot sijoitetaan hakemistoon [user_login]/jserv/servlets.
Servlettien tarvitsemat apuluokat kannattaa määritellä Java-pakkaukseksi (alla esimerkissä pakkaus apu). Pakkaushakemistoon osoittava polku on liitettävä CLASSPATH:iin esim lisäämällä environment tiedostoon rivi:
$assist=/home/[user_login]/apu
ja muuttamalla viimeinen rivi muotoon
export CLASSPATH=$jsdk:$jserv:$assist:$CLASSPATHJos haluat jättää ajoympäristön käyntiin pidemmäksi kuin oletusajaksi (esim. työn tarkastusta varten) on lifetime muuttujan arvoa muutettava.
Servlettien lähdekoodi
Lähdekoodia suositellaan pidettäväksi erillään suoritusympäristöstä. Lähdekoodin sijainnilla ei ole kuitenkaan servlettien toiminnan kannalta merkitystä. Suoritusta varten on käännetyt *.class tiedostot kopioitava asennushakemistoon.
Servlettien kääntäminen
Käännettäessä Java-ohjelmaa täytyy kaikkien ohjelman käyttämien luokkien ja kirjastojen löytyä CLASSPATH:sta.
- Jserv-ympäristössä toimittaessa saa tiedostossa [user_login]/jserv/etc/environment määritellyt kirjastot käännöstä varten CLASSPATH:iin komennolla setup servlet. Tämä komento tarvitaan vain kerran istunnossa. Koska jserv-käynnistys muokkaa myös CLASSPATH:ia olisi käynnistys hyvä tehdä eri istunnosta kuin käännökset.
- Tomcat ympäristöä varten luotaneen komento setup tomcat, jolla hakemistoon SERVLET/WEB-INF/lib/ sijoitetut kirjastot saa mukaan CLASSPATHiin käännöstä varten.
Servlettiympäristön käynnistys ja sammutus
Servlettiympäristö käynnistetään jserv -ympäristössä komennolla start-servlet ja sammutetaan komennolla stop-servlet ja Tomcat-ympäristössä vastaavasti komennoilla start-tomcat ja stop-tomcat. Ympäristö pysyy pystyssä oletusarvoisesti 10 tuntia ja sammuu sitten itsekseen. Käyttöönottoskriptit wanna-servlet ja wanna-tomcat antavat lisäohjeita käynnistyksestä ja sammutuksesta.
Viittaaminen servlettiin
Jserv-ympäristössä toimivaan servlettiin viitataan url:lla http://db.cs.helsinki.fi/s/[user_login]/servlet_class_name.
Tomcat-ympäristössä toimivaan servlettiin viitataan url:lla http://db.cs.helsinki.fi/t/[user_login]/servlet_class_name.
Esimerkiksi http://db.cs.helsinki.fi/s/laine/PgTesti käynnistää servletin PgTesti jserv-ympäristössä.
Servletin palvelut toteutetaan servlettiluokan palvelumetodien doPost tai doGet avulla. Saadessaan servlettiin kohdistuvan palvelupyynnön servlettiajuri käynnistää palvelumetodin erillisenä rinnakkaisena säikeenä, ts. samaa palvelumetodia voi samanaikaisesti olla käynnissä useita rinnakkain. 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 (servlet.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ä. jserv toimii version 2.0 kanssa.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 pitäisi servlettien suoritusta varten kopioida hakemistoon SERVLET/WEB-INF/lib/ (edellä SERVLET tarkoittaa servlettihakemistoa). Ajuri löytyy löytyy paikasta/opt/jdbc/oracle/classes12.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 wanna-postgres asennusskriptin antama porttinumero ja username kayttäjän unix-käyttäjätunnus.Tietokanta-ajuri PostgreSQL:lle on tiedostossa /opt/jdbc/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ä