Java-servletit

 

Teemu Katajisto

 

 

 

 

 

Tietotekniikan LuK-tutkielma

14.12.2000

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Jyväskylän yliopisto

Tietotekniikan laitos

 


 

 

Tekijä: Teemu Katajisto

 

Yhteystiedot: Sähköposti teekata@st.jyu.fi

 

Työn nimi: Java-servletit

 

Title in English: Java Servlet

 

Työ: LuK-tutkielma

 

Linja: Ohjelmistotekniikka

 

Teettäjä: Jyväskylän yliopisto, tietotekniikan laitos

 

Tiivistelmä: LuK-tutkielma kuvaa Java Servlet -tekniikan perusteita. Tutkielmassa käsitellään pääasiassa HTTP-servlettejä sekä niihin liittyen istunnonhallintaa, avusteita ja säikeitä.

 

Avainsanat: Java, servletti, HTTP-servletti, istunnon hallinta, JSDK

 

Keywords: Java, servlet, HTTP-servlet, session management, JSDK

 

 


Sisällysluettelo

 

1.     Johdanto. 1

2.     Termit 1

3.     Johdatus servletteihin. 2

3.1.      Mikä on servletti?. 2

3.2.      Servletit vai CGI-tekniikka. 3

3.3.      Servletin perusrakenne. 3

3.4.      Servletin elämänkaari 4

3.5.      Luokkahierarkia. 6

4.     Servlet API -rajapinnan perusteet 7

4.1.      Ensimmäinen servletti 7

4.2.      HTTP-servletit 8

4.3.      HTTP-servletin liittymäluokat 9

4.4.      Esimerkki proxy-palvelin -servletistä. 10

4.5.      Säikeiden käytöstä servleteissä. 11

5.     Istunnon hallinta. 12

5.1.      Tilan seuranta. 12

5.2.      Avusteet 13

5.3.      Avusteiden asettaminen Servlet APIn avulla. 14

6.     Asennus ja käyttö. 15

6.1.      JSDK:n asennus. 15

6.2.      JSDK:n Servlet Runner 15

6.3.      Java-servlettejä tukevat WWW-palvelimet 17

6.4.      Servlettikoneet 17

7.     Yhteenveto. 17

Lähteet 18

 


1.      Johdanto

Alun perin WWW-sivut olivat staattisia HTML-dokumentteja, jotka tallennettiin tiedostoihin WWW-palvelimille. Näiden sivujen palauttamaa dataa ei voinut muuttaa tilanteen mukaan. Tämän takia kehitettiin CGI-rajapinta, joka HTML:n lomakkeiden (engl. form) avulla  mahdollisti dynaamisten WWW-sivujen luonnin. Tämä mahdollisti interaktiivisten WWW-sovellusten rakentamisen suoraan WWW-palvelinten laajennuksina, jolloin sovelluksia voitiin käyttää suoraan Internet-yhteyden kautta WWW-selaimella.

 

Verkossa tarjottavien palveluiden, kuten sähköisen kaupankäynnin, kasvaessa myös tekniikoiden on kehityttävä entistä turvallisemmiksi ja luotettavimmiksi. Lisäksi palveluiden tuottajien kannalta olennaisia asioita ovat siirrettävyys ja tehokkuus. Myöhemmin CGI:n rinnalle on kehitetty uusia tekniikoita, joilla pyritään välttämään CGI:n ongelmat, kuten esimerkiksi siirrettävyys. Näistä varsinkin Javan servletit ovat tehokas ja käyttöjärjestelmästä riippumaton tekniikka dynaamisten WWW-sivujen luomiseen.

 

Tässä tutkielmassa selvitetään servlettien perusteet, tavallisten servlettien ja HTTP-servlettien tekeminen ja asennus sekä mitä servleteillä voi yleensäkin tehdä. Lisäksi kerrotaan lyhyesti servlettien ohjelmoinnin turvallisuusnäkökulmista, säikeistä ja servletin tilan seurannasta.

 

Luvussa 2 esitellään tutkielmassa käytetyt termit ja lyhenteet. Luku 3 käsittelee servlettien perusteita, ja luvussa 4 perehdytään tarkemmin Java Servlet -rajapinnan luokkiin ja metodeihin. Luvussa 5 kuvataan servlettien istunnon hallinnan menetelmiä. Luvussa 6 tarkastellaan servlettien asennusta ja käyttöönottoa sekä luetellaan servlettejä tukevia palvelimia.

2.      Termit

Luvussa esitellään lyhyesti tutkielmassa kätetyt termit ja lyhenteet.

 

Appletti eli sovelma on pieni Javalla tehty ohjelma, jota voidaan ajaa Javaa tukevilla WWW-selaimilla.

 

CGI eli Common Gateway Interface on WWW-palvelimen rajapinta, jonka avulla voidaan luoda interaktiivisia WWW-sovelluksia.

 

HTML eli Hyper Text Markup Language on kieli, jossa HTML-tiedostojen perusteella selaimet muodostavat WWW-sivun.

 

Java on siirrettävä ja turvallinen olio-ohjelmointikieli.

JDK eli Java Development Kit on kehitysympäristö, joka sisältää välineet Java-ohjelmien tekemiseen, kääntämiseen ja ajamiseen.

 

JSDK eli Java Servlet Development Kit on kehitysympäristö, jota tarvitaan servlettien tekemiseen ja ajamiseen.

 

JVM eli Java Virtual Machine on virtuaalikone, joka ajaa Java-ohjelmia.

 

Servletti eli palvelma on pieni ohjelma, joka on tehty Javalla laajentamaan palvelimen toiminnallisuutta. Servletit ajetaan palvelimella.

 

Servlet API on rajapinta servlettien toteuttamiseen palvelimilla.

 

WWW eli World Wide Web on Internetin graafinen käyttöliittymä.

3.      Johdatus servletteihin

Luvussa esitellään servlettien toteuttamiseen tarvittavat perusteet sekä kerrotaan hieman servlettien arkkitehtuurista ja elämänkaaresta. Luku perustuu pääasiallisesti lähteisiin [Callaway] ja [Campione].

3.1.        Mikä on servletti?

Servletti on Javalla ohjelmoitu ohjelmakomponentti, jolla dynaamisesti laajennetaan palvelimen toiminnallisuutta [Callaway]. Servletit toimivat Javaa tukevilla WWW-palvelimilla, kuten appletit Javaa tukevilla WWW-selaimilla. Servletit eivät kuitenkaan toimi graafisen käyttöliittymän avulla applettien tapaan, vaan ne toimivat palvelimen taustalla ja vasta niiden prosessoimat tulosteet näytetään käyttäjälle (katso kuva 1).

 

Servletit ovat Java-luokkia. Niille on ennalta määritelty Servlet API -ohjelmointi-rajapinta, jonka avulla palvelin voi kutsua servlettiä. Servlettien käyttöä ei ole rajoitettu WWW-palvelimiin, vaan minkä tahansa Servlet APIa tukevan palvelimen toiminnallisuutta voidaan laajentaa servlettien avulla. Nämä voivat olla esimerkiksi

ftp-, telnet- tai uutisryhmäpalvelimia.

Kuva 1. Selaimen ja servletin välinen liikenne.

 

Servletit määräävät raamit, jonka avulla toteutetaan pyyntö/vastaus -tyyppisiä (engl. request/response) sovelluksia. Tällaisia ovat esimerkiksi HTML-sivujen muodostaminen ja tulostaminen käyttäjäkohtaisesti, käyttäjien tunnistus ja muut tietoturvasovellukset, pyyntöjen hajauttaminen eri palvelimille resurssien säästämiseksi sekä monen asiakassovelluksen lähettämän tiedon julkaiseminen (esim. online-ilmoitustaulu).

3.2.        Servletit vai CGI-tekniikka

Servlettien edut CGI:hin nähden tulevat esiin suorituskyvyssä, siirrettävyydessä ja turvallisuudessa [Callaway]. Näistä suorituskyky on ehkä kaikkein näkyvin parannus, sillä useimmat servletit ajetaan samassa prosessiavaruudessa kuin palvelin ja ne ladataan vain kerran palvelimen käynnistyessä. Tällöin ne pystyvät vastaamaan asiakassovellusten pyyntöihin mahdollisimman tehokkaasti ja nopeasti. CGI joutuu luomaan uuden prosessin jokaista pyyntöä kohti, josta aiheutuu huomattava kustannus verrattuna servlettien tapaan. Tämä näkyy selvimmin tietokantasovelluksissa, joissa servletti voi jakaa saman tietokantayhteyden useamman pyynnön kanssa, joka taas CGI:llä on mahdotonta.

 

Toinen selvä etu on siirrettävyys. Servlettejä voidaan ajaa erilaisilla palvelimilla ja erilaisissa ympäristöissä ilman muutoksia. Tämä on erityisen tärkeää tehtäessä hajautettuja sovelluksia.

 

Myös turvallisuusnäkökohdasta katsoen servletit tarjoavat etuja verrattuna CGI:hin. Epäluotettavat servletit (joita ei ole esim. digitaalisesti allekirjoitettu) ajetaan omassa ”hiekkalaatikossa” (engl. sandbox) eli suojatussa muistiavaruudessa, jolloin ne eivät voi käsitellä ohjelman ulkopuolisia resursseja. Näitä asetuksia voi tarvittaessa muuttaa Javan turvallisuusmanagerin (engl. security manager) avulla.

3.3.        Servletin perusrakenne

Servlettien perusrakenne on melko yksinkertainen. Ne toteuttavat joko GenericServlet- tai HttpServlet-luokkien rajapinnan ja ylikirjoittavat ainakin yhden metodin, jossa servletin toiminnallisuus toteutetaan.

 

Tämä metodi voi olla service, jota automaattisesti kutsutaan jokaisen asiakassovelluksen pyynnön saapuessa. HTTP-servlettien ollessa kyseessä tätä metodia ei kuitenkaan yleensä ylikirjoiteta, vaan ylikirjoitetaan muita metodeja riippuen HTTP-pyynnön tyypistä (GET, POST, HEAD, PUT tai DELETE). Yleensä servleteissä ylikirjoitetaan myös metodit init ja destroy.

 

Tavallisen servletin perusrakenne on lyhyesti esitettynä seuraavanlainen:

 

public class MyServlet extends GenericServlet

{

            public void init()

            {

            // alustus

            }

 

            public void service()

            {

                  // toiminnallisuus

            }

 

            public void destroy()

            {

                  // resurssien vapautus

            }

}

 

Servletin metodeista init-metodia kutsutaan, kun servletti ladataan ensimmäisen kerran. Tämä metodi vastaa luokan rakentajafunktiota eli konstruktoria, jossa suoritetaan servletin alustustoimenpiteet. Varatut resurssit, kuten esimerkiksi tietokantayhteydet, vapautetaan destroy-metodissa. Se vastaa siis toiminnaltaan luokan tuhoajafunktiota eli destruktoria. Näitä metodeja ei ole pakko ylikirjoittaa.

 

Servletin oma toiminnallisuus toteutetaan service-metodissa tai HTTP-servletin ollessa kyseessä ylikirjoitetaan esim. doGet-metodi, joka vastaa HTTP-pyynnön tyyppiä GET. Tavalliset servletit eroavat HTTP-servleteistä myös hieman perusmetodien välittämien parametrien ja poikkeusten tyypeissä. Edellä esitetyssä perusrakenteessa niitä ei kuitenkaan ole yksinkertaisuuden säilyttämisen takia käytetty.

3.4.         Servletin elämänkaari

Servletin elämänkaari voidaan jakaa yhdeksään osaan [Callaway]:

 

  1. Palvelin lataa servletin, kun sitä ensimmäisen kerran kutsutaan tai palvelimen käynnistyessä, jos näin on määritelty. Servletin ei tarvitse olla paikallisella palvelimella,vaan se voidaan ladata myös muualta.

 

  1. Palvelin luo yhden tai useamman instanssin servletistä. Servletin toteutuksesta riippuen palvelin voi luoda yhden instanssin, joka palvelee kaikkia pyyntöjä useiden säikeiden avulla, tai useita instansseja, joista yksi valitaan kulloinkin suorittamaan asiakassovelluksen pyyntöä.

 

  1. Palvelin luo ServletConfig-olion, jonka tehtävänä on muodostaa alustusinformaatio servletille.

 

  1. Palvelin kutsuu servletin init-metodia välittäen parametrina luodun ServletConfig-olion. Metodi suoritetaan ennenkuin ensimmäinen asiakassovelluksen pyyntö käsitellään. Jos servletistä luotiin useita instansseja, init-metodia kutsutaan erikseen jokaiselle instanssille.

 

  1. Palvelin luo asiakkaan pyynnön välittämästä datasta ServletRequest- tai HttpServletRequest-olion. Se luo myös ServletResponse- tai HttpServletResponse-oliot, joiden avulla muodostetaan servletin vastaus pyyntöön. Välitetyn olion tyyppi riippuu siitä, käytetäänkö GenericServlet- vaiko HttpServlet-luokkaa.

 

  1. Palvelin kutsuu servletin service-metodia (tai esim. doGet- tai doPost-metodeita HTTP-servleteille) välittäen parametreina kohdassa 5 luodut oliot.

 

  1. service-metodi prosessoi asiakassovelluksen pyynnön käyttämällä ServletRequest- tai HttpServletRequest-oliota ja vastauksen käyttämällä ServletResponse- tai HttpServletResponse-metodia.

 

  1. Prosessi alkaa uudelleen kohdasta 5, jos palvelin saa uuden pyynnön asiakassovellukselta.

 

  1. Palvelin kutsuu destroy-metodia, kun servletti ajetaan alas. Servletin alasajo voidaan hoitaa ohjelmallisesti tai ylläpitäjän toimesta. Tämän jälkeen servletti kelpuutetaan roskien keruuseen (engl. garbage collection).

 

Nämä yhdeksän askelta (katso kuva 2) kuvaavat koko servletin elämänkaaren, vaikka ne saattavatkin hieman vaihdella riippuen palvelimen servlettituen toteutuksesta.

 

 

Kuva 2. Servletin elämänkaari.


3.5.        Luokkahierarkia

Servlettien rakentamiseen ja ajamiseen tarvittavat luokat ja rajapinnat sisältyvät kahteen pakettiin: javax.servlet ja javax.servlet.http. Nämä paketit tunnetaan yhteisnimellä Java Servlet API. Kuvassa 3 on esitelty näiden pakettien luokkahierarkia ja suhteet muihin Javan luokkiin.

 

 

Kuva 3. Servlet APIn luokkahierarkia.

 

Paketissa javax.servlet ovat ne luokat ja liittymäluokat, joita tarvitaan yleisten servlettien rakentamiseen. Paketti sisältää myös poikkeusluokat, joita servlettien metodit voivat aiheuttaa. Paketissa javax.servlet.http on näistä perittyjä luokkia ja liittymäluokkia, joihin on lisätty HTTP-protokollan käsittelyyn liittyviä ominaisuuksia.

 

Servlet APIn yleisimmin käytetyt luokat ovat GenericServlet ja HttpServlet, joita laajentamalla servletit yleensä toteutetaan. Lisäksi servlettien välittämää dataa käsitellään yleisimmin liittymäluokkien ServletRequest ja HttpServletRequest sekä ServletResponse ja HttpServletResponse avulla. Seuraavassa luvussa on kuvattu tarkemmin näiden luokkien käyttöä.

4.      Servlet API -rajapinnan perusteet

Luvussa käydään läpi esimerkin avulla tavallisen servletin toteutuksessa tarvittavat metodit ja periaatteet. Tämän jälkeen kerrotaan metodit, jotka HTTP-servletin pitää toteuttaa, jotta se voisi vastata asiakassovelluksen pyyntöihin. Lopuksi puhutaan lyhyesti säikeistä ja niiden turvallisesta käytöstä servleteissä. Luku perustuu pääasiallisesti lähteisiin [Callaway], [Campione] ja [Cornell].

4.1.        Ensimmäinen servletti

Seuraavassa on yksinkertainen esimerkki GenericServlet-luokan perivästä servletistä, joka tulostaa tekstin ”Hei maailma!” sisältävän HTML-koodin.

 

import java.io.*;

import javax.servlet.*;

 

public class MyServlet extends GenericServlet {

 

  public void init() throws ServletException {}

 

  public void destroy() {}

 

  public void service(ServletRequest req,

                      ServletResponse res

  ) throws ServletException, IOException  {

    res.setContentType( "text/html" );

    PrintWriter out = res.getWriter();

    out.println( "<html>" );

    out.println( "<head> );

    out.println( "<title>Esimerkkiservletti</title>" );

    out.println( "</head>" );

    out.println( "<body>" );

    out.println( "<h1>Hei maailma!</h1>" );

    out.println( "</body>" );

    out.println( "</html>" );

    out.close();

  }

}

 

Esimerkin kahdella ensimmäisellä rivillä otetaan käyttöön tarvittavat Java-paketit io ja servlet.

 

Seuraavalla rivillä määritellään MyServlet-luokka. Jokainen servletti joko toteuttaa Servlet-rajapinnan tai periytyy GenericServlet- tai HttpServlet-luokista. Vaikka servlettejä voidaan tehdä toteuttamalla Servlet-rajapinta, ei sitä kuitenkaan yleensä käytetä.

 

Tämän jälkeen ylikirjoitetaan init-metodi. Metodi suoritetaan kerran jokaiselle instanssille. Tämän jälkeen servletti pysyy palvelimen muistissa, kunnes se ajetaan alas. Jos palvelin muodostaa servletistä vain yhden instanssin, niin silloin palvelin luo uuden säikeen jokaista service-metodin kutsua varten. Tämä voidaan estää toteuttamalla SingleThreadModel-rajapinta (ks. luku 4.5). init-metodia voidaan käyttää servlettien välisten jaettujen resurssien (esim. tietokantayhteyksien) muodostamiseen staattisten muuttujien avulla.

 

service-metodin ensimmäinen parametri ServletRequest sisältää asiakaspyynnön datan, kuten käytetyn protokollan otsikot. Näitä voidaan lukea käyttämällä ServletRequest-luokan metodeja. Vastaavasti voidaan toisena parametrina välitetyn ServletResponse-olion sisältämää vastausta asiakaspyyntöön muuttaa ServletResponse-luokan metodeilla. Servletti ei saa käsitellä IOExeption-poikkeusta, vaan se on aina välitettävä eteenpäin palvelimen käsiteltäväksi.

 

Ensimmäisenä service-metodissa asetetaan HTTP-otsikon content-type-kentän arvoksi text/html. Kenttä content-type on asetettava ennenkuin varsinaista dataa lähetetään takaisin selaimelle. Tämän jälkeen avataan kirjoitusvirta getWriter-metodilla. Jos vastaukseen haluaa liittää binääristä dataa, niin silloin on käytettävä getOutputStream-metodia, joka palauttaa viittauksen  ServletOutputStream-olioon.

 

Seuraavaksi kirjoitetaan kirjoitusvirtaan println-metodilla haluttu HTML-koodi. Lopuksi pitää aina muistaa sulkea avattu kirjoitusvirta. Jos servletissä käytetään poikkeusten käsittelyä, niin silloin luonnollinen paikka virran sulkemiseen olisi finally-lohko, jolloin se tulisi aina suoritettua. Kuvassa 4 on esimerkin tulostama HTML-sivu.

 

 

Kuva 4. Hei maailma! -servletin tulostus.

4.2.        HTTP-servletit

Paketista javax.servlet.http löytyvä HttpServlet-luokka on laajennettu GenericServlet-luokasta. HttpServlet-luokkaan on lisätty metodeita, joilla pystytään käsittelemään HTTP-protokollan pyyntöja. Lisäksi paketista javax.servlet.http löytyy liittymäluokkia ja luokkia avusteiden sekä HTTP-istuntojen ja -otsikoiden käsittelyyn.

 

Tavallisessa servletissä oli ylikirjoitettava ainoastaan metodi service, jota palvelin kutsui servlettiä pyydettäessä. HTTP-servletissäkin voidaan käyttää tätä metodia, mutta suositeltavampaa on käyttää suoraan HTTP-pyyntöä vastaavia metodeita. Nämä ovat doPost, doGet, doHead, doPut ja doDelete. Jos palvelin saa sellaisen pyynnön, jota ei ole tuettu servletissä, niin silloin palvelin generoi selaimelle näytettävän HTTP 400 ”Bad Request” –viestin.

 

Taulukossa 1 on kuvattu ylikirjoitettavat metodit ja luvussa 4.4 on esimerkki proxy-palvelin -servletistä, joka toteuttaa doGet-metodin. Lisäksi on olemassa metodit HTTP TRACE ja HTTP ALLOWS -pyyntöjen käsittelyyn. Näitä ei kuitenkaan yleensä ole tarvetta ylikirjoittaa.

 

Metodi

Kuvaus

service

Metodia kutsutaan palvelimen toimesta, kun servlettiä pyydetään. Jos service-metodia ei ylikirjoiteta, sen oletustehtävänä on kutsua yhtä alla olevista do-metodeista riippuen HTTP-pyynnön tyypistä.

doGet

Metodia kutsutaan vastaamaan HTTP GET -pyyntöön. Metodin toteuttaminen antaa automaattisesti tuen myös HTTP HEAD -pyynnöille.

doHead

Metodia kutsutaan vastaamaan HTTP HEAD -pyyntöön. Jos metodia ei ole ylikirjoitettu ja saadaan HEAD-pyyntö, niin silloin oletuksena kutsutaan doGet-metodia luomaan tarvittavat HTTP-otsikkokentät, jotka palautetaan pyytäjälle.

doPost

Metodia kutsutaan vastaamaan HTTP POST -pyyntöön.

doPut

Metodia kutsutaan vastaamaan HTTP PUT -pyyntöön.

doDelete

Metodia kutsutaan vastaamaan HTTP DELETE -pyyntöön.

 

Taulukko 1. Servlettien ylikirjoitettavia metodeita.

 

HTTP-servletit toteutetaan siis laajentamalla HttpServlet-luokkaa, joka taas perii perustoimintonsa GenericServlet-luokalta. Tämän luokan metodeista käyttökelpoisimpia ovat

 

public String getInitParameter(String name)

public Enumeration getInitParameterNames()

public void log(String msg).

 

Näistä kahdella ensimmäisellä voidaan lukea palvelimen servletille välittämiä alustustietoja ja viimeisellä voidaan kirjoittaa servletin viestejä lokitiedostoon. Tiedoston nimi ja paikka riippuvat käytetystä palvelimesta.

4.3.        HTTP-servletin liittymäluokat

Luokat HttpServletRequest ja HttpServletResponse laajentavat vastaavien ServletRequest- ja ServletResponse-luokkien toiminnallisuutta.

 

HttpServletRequest-olio välitetään parametrina service- ja do-metodeille. Lisäksi se määrittelee metodit, jotka kuvaavat tehdyn HTTP-pyynnön. Olion kapseloima data sisältää HTTP-otsikon tiedot, kuten HTTP-pyynnön tyypin, avusteet, autentikointitiedot sekä HTML-lomakkeella välitetyt parametrit. Käytetyimpiä metodeita ovat

 

public Cookie[] getCookies(),

 

jota käytetään avusteiden yhteydessä (ks. luku 5.2), sekä

 

public String getQueryString(),

 

jolla saadaan HTTP POST -metodin välittämät parametrit selville otsikosta. Tämä voidaan parsia Hashtableksi HttpUtils-luokan staattisella metodilla

 

public static Hashtable parseQueryString( String queryString ).

 

HttpServletResponse-olio välitetään myös parametrina service- ja do-metodeille. Servletti voi käyttää tätä oliota vastauksessaan HTTP-pyyntöön. Sen metodien avulla voidaan asettaa avusteita, muuttaa vastauksen otsikkokenttiä ja välittää uudelleensuuntausohjeita. Käytetyimpiä metodeita ovat

 

public void addCookie( Cookie cookie ),

 

jolla voidaan asettaa avuste vastaukseen (ks. luku 5.2) sekä

 

public void sendError( int status, String message) throws IOException,

 

jota käytetään virhetilanteissa palauttaamaan oikea statuskoodi selaimelle. Statuskoodit on määritelty julkisiksi staattisiksi vakioiksi luokan sisälle. Esimerkkejä näiden käytöstä on luvussa 4.4.

4.4.        Esimerkki proxy-palvelin -servletistä

Seuraavassa on hieman monipuolisempi esimerkki proxy-palvelin -servletistä. Sitä voidaan käyttää esimerkiksi HTTP-palvelimilla kiertämään applettien rajoitusta ottaa yhteyttä ainoastaan siihen palvelimeen, josta se on ladattu. Tässä esimerkissä servletti lataa pyydetyn URL-osoitteen ja palauttaa sen sisällön selaimelle.

 

import java.io.* ;

import java.net.* ;

import javax.servlet.* ;

import javax.servlet.http.* ;

 

public class ProxyServer extends HttpServlet {

public void doGet( HttpServletRequest req,

            HttpServletResponse res )

                        throws ServletException, IOException

{

            String query = null;

     

            res.setContentType(“text/html”);

            PrintWriter out = response.getWriter();

            // Selvitetään pyynnöstä haettava URL

            query = req.getParameter(“URL”);

            if ( query == null ){

                  res.sendError(HttpServletResponse.SC_BAD_REQUEST,

                                “Missing URL parameter”);

                  return;

            }

     

            try {

                  query = URLDecoder.decode(query);

            }

            catch( Exception exception) {

res.sendError(HttpServletResponse.SC_BAD_REQUEST,

                                “URL decode error “ + exception);

                  return;

}

// Luetaan data URL:stä ja tulostetaan sivu

try {

            URL url = new URL(query);

            BufferedReader in = new BufferedReader(

                  new InputStreamReader(url.openStream()));

     

            String line;

            while ( (line = in.readLine()) != null ) {

                  out.println(line);

            }

            out.flush();

}

catch( IOException exception ) {

res.sendError(HttpServletResponse.SC_NOT_FOUND,

                    “Exception: “ + exception);

                  }

}

}

4.5.        Säikeiden käytöstä servleteissä

Java-ohjelmien yksi ydinhuolenaihe on säieturvallisuus (engl. thread safety). Tällä tarkoitetaan sitä, että useampi säie ei yhtäaikaa pysty muuttamaan tai lukemaan samaa dataa. Servletin rajapinta ei toteuta säieturvallisuutta, vaan siihen on käytettävä Javan tarjoamia keinoja. Näitä ovat säikeiden synkronointi, lokaalien muuttujien sekä SingleThreadModel-rajapinnan käyttö.

 

Synkronoinnilla voidaan luokan sisäinen muuttuja lukita niin, etteivät muut säikeet pysty sitä muuttamaan. Tämä voidaan toteuttaa siten, että määritellään synkronointilohko koodiin seuraavasti:

 

            synchronized (this) { ... },

 

jolloin vain yksi säie kerrallaan voi suorittaa kyseisen lohkon. Myös koko metodi voidaan synkronoida määrittelyllä

 

            public synchronized void service() { ... },

 

jolloin vain yksi säie kerrallaan voi suorittaa service-metodin. Tämä ei tietenkään ole kovin kannattavaa tehokkuussyistä monen käyttäjän järjestelmissä.

 

Paikallisia muuttujia voidaan käyttää service-, doGet- ja doPost-metodien sisällä luokan sisäisen muuttujan sijaan. Tällöin jokaisella säikeellä on yksi oma kopio kyseisestä muuttujasta, eivätkä muut säikeet pysty muuttamaan sen arvoa.

 

Yksinkertaisin tapa toteuttaa säieturvallisuus on käyttää SingleThreadModel-rajapintaa. Rajapinta ei määrittele metodeja, vaan toimii lippuna palvelimelle aiheuttaen sen, että ainoastaan yksi säie kerrallaan pystyy ajamaan servletin service-, doPost- tai doGet-metodin.

 

Myös tästä saattaa aiheutua tehokkuusongelmia, vaikka palvelin muodostaakin SingleThreadModel-rajapintaa käyttävästä servletistä useita instansseja valmiiksi, jonka jälkeen niistä valitaan yksi vuorollaan palvelemaan samanaikaisia pyyntöjä. Koska valmiiden instanssien määrä on rajattu (määriteltävissä palvelinkohtaisesti), joudutaan vapautuvaa instanssia odottamaan, kun kaikki instanssit ovat jo käytössä. Rajapinta saadaan käyttöön kirjoittamalla servletin luokan määrittelyssä

 

public class MyServlet extends GenericServlet implements

SingleThreadModel { ... }.

 

SingleThreadModel-rajapinnan käyttö on suositeltavaa, kun pyyntöjen määrän tiedetään pysyvän alhaisena.

5.      Istunnon hallinta

HTTP on tilaton protokolla. Tällä tarkoitetaan sitä, että palvelin ei muista edellisiä asiakassovelluksen yhteydenottoja, vaan jokainen palvelupyyntö on edellisistä pyynnöistä riippumaton istunto.

 

Tämä aiheuttaa ongelmia esimerkiksi sellaisissa tilanteissa, joissa asiakas pitää tunnistaa ennenkuin palvelupyyntö voidaan toteuttaa. Tällöin palvelimen on pyydettävä asiakkaalta tunnistustiedot joka pyynnön yhteydessä. Tällainen tilanne voidaan ratkaista joko tallentamalla tieto asiakkaasta URL-polkuun käyttämällä piilotettuja muuttujia (engl. hidden variables) tai käyttämällä avusteita ja servlettien yhteydessä myös suoraan Servlet APIn metodeita. Tämä luku perustuu lähteeseen [Callaway].

5.1.        Tilan seuranta

Servlet API tukee suoraan istunnon hallintaa HttpSession-liittymäluokan avulla. HttpSession-olio kapseloi HTTP-istunnon tiedot, kuten istunnon ID:n ja muut asiakkaaseen littyvät tiedot. Olio saadaan käyttöön kutsumalla HttpServletRequest-olion metodia getSession seuraavalla tavalla:

 

HttpSession mySession = request.getSession(true);

 

Kutsussa boolean arvo true kertoo, että jos istuntoa ei ole, niin luodaan uusi. Tämän jälkeen HttpSession-olion metodeilla voidaan käsitellä istunnon dataa. Servlet API käyttää istunnon hallintaan oletuksena avusteita. Jos selain ei tue avusteita, niin silloin käytetään URL-polkuun tallentamista.

5.2.        Avusteet

Avusteet ovat yksinkertainen mekanismi käyttäjäkohtaisen datan tallentamiseen ja lukemiseen WWW-sovelluksissa. HTTP-palvelimen palauttaessa pyyntöä selaimelle se voi liittää mukaan informaatiota. Informaatio tallennetaan selaimelle, jos selain sallii tämän. Informaation sisältävän URL-osoitteen perusteella selain tietää, mille palvelimelle avuste pitää palauttaa. Selain tarkistaa jokaisen HTTP-yhteyden muodostuksen yhteydessä, onko sille tallennettu kyseisen palvelimen toimesta avuste. Jos avuste löytyy, se liitetään selaimen pyyntöön.

 

HTTP-otsikon syntaksi avusteen asettamiselle on

 

Set-Cookie: <NIMI>=<ARVO>; expires=<PÄIVÄMÄÄRÄ>; domain=<TOIMIALUE>; path=<POLKU>; secure

 

HTTP-otsikon syntaksi avusteen palauttamiselle on

 

Cookie: <NIMI1>=<ARVO1>; <NIMI2>=<ARVO2>; ...

 

Otsikon Set-Cookie attribuutit on selitetty taulukossa 2.

 


Attribuutti

Selitys

<NIMI>=<ARVO>

Attribuuttipari vastaa WWW-muuttujia, jotka lähetetään HTTP POST -operaatiossa. NIMI on avusteen nimi ja ARVO vastaavasti avusteeseen tallennettu arvo.

expires=<PVM>

Attribuutti ilmaisee milloin avuste vanhenee. Kun avuste vanhenee, se poistetaan selaimelta eikä sitä enää käytetä. Jos attribuuttia ei anneta, niin avuste vanhenee selain suljettaessa. Attribuutin formaatti on Weekday, DD-Mon-YY HH:MM:SS GMT

domain=<TOIMIALUE>

Attribuuti ilmoittaa palvelimen toimialueen mihin avuste palautetaan. Esimerkiksi domain=jyu.fi palauttaisi avusteen mm. osoitteisiin tukki.jyu.fi ja www.jyu.fi. Toimialueen on oltava ainakin kaksiosainen.

path=<POLKU>

Attribuuti asettaa avusteen polun selaimella. Polun on myös sisällyttävä ja täsmättävä domain-attribuuttiin. Yleisimmin käytetty arvo on ”/”.

secure

Attribuutti ilmaisee, että avuste voidaan lähettää ainoastaan salatun yhteyden kautta, kuten esimerkiksi HTTPS (Secure HTTP). Muussa tapauksessa avustetta ei lähetetä.

 

Taulukko 2. Set-Cookie -otsikon attribuutit.


 

5.3.        Avusteiden asettaminen Servlet APIn avulla

HttpServletResponse-liittymäluokka määrittelee metodin setHeader, jonka avulla voidaan asettaa HTTP-otsikon kenttien arvoja. Yksinkertaisempaa on kuitenkin käyttää avusteiden lähettämiseen luokan javax.servlet.http.Cookie metodeja.  Se sisältää metodit avusteen sisältämän tiedon muokkaamiseen sekä attribuuttien asettamiseen.

 

Avusteita käytettäessä on huomioitava se, että asetettaessa kaksi avustetta, joilla on sama nimi ja polku, niin viimeisenä asetettu korvaa ensin asetetun. Avusteet voivat olla olemassa yhtäaikaa, jos polut eroavat toisistaan. Lisäksi on hyvä muistaa, että selain voi tallentaa maksimissaan kolmesataa avustetta, joista samalta palvelimelta voi olla maksimissaan kaksikymmentä.

 

Avuste luodaan yksinkertaisesti kutsulla

 

Cookie myCookie = new Cookie(”testi”, ”testiarvo”)

 

joka asettaa avusteen nimeksi “testi” ja arvoksi “testiarvo”.  Tämän jälkeen avusteelle voidaan asettaa muita attribuutteja kutsuilla

 

myCookie.setDomain(“jyu.fi”);

myCookie.setPath(“/”);

myCookie.setSecure(“true”);

 

Avuste lisätään HTTP-otsikkoon service-, doPost- ja doGet-metodien välittämän HttpServletResponse-olion avulla. Tästä luokasta löytyy metodi addCookie, jolla lisäys hoidetaan seuraavasti:

 

response.addCookie(myCookie);

 

Vastaavasti saadaan selville selaimen palauttamat avusteet käyttämällä HttpServletRequest-olion metodia getCookies, joka palauttaa taulukon selaimen palauttamista avusteista seuraavasti:

 

Cookies[] myCookies = request.getCookies();

 

Tässä esiteltyjen metodien lisäksi Cookie-luokasta löytyy monia muitakin käyttökelpoisia metodeja avusteiden käsittelyyn.


6.      Asennus ja käyttö

Kirjoitettujen servlettien testaaminen ja ajaminen vaatii ensin servletin asentamisen palvelinkoneelle, jonka jälkeen voidaan selaimella pyytää palvelimelta servletin ajamista. Jos käytössä ei ole servlettejä tukevaa WWW-palvelinta, voi servlettejä testata myös esimerkiksi JSDK:n (Java Servlet Development Kit) mukana tulevan Servlet Runnerin avulla. Seuraavassa on lyhyesti esitelty näitä menetelmiä tarkemmin. Luku perustuu pääasiassa lähteisiin [Callaway] ja [Magelan Institute].

6.1.        JSDK:n asennus

Servlettien kääntämiseen vaaditaan JDK:n mukana tulevan Java-ympäristön lisäksi myös Java-servlettien laajennuspaketti, JSDK. Tämä sisältää servlettiluokat ja joitakin hyödyllisiä apuohjelmia.

 

JSDK:n asentamisen jälkeen pitää Java-kääntäjälle kertoa, missä servlettiluokat sijaitsevat. Tämä tehdään asettamalla CLASSPATH-ympäristömuuttuja osoittamaan oikeaan paikkaan. Unixin C-shellissä tämä tapahtuu komennolla

 

setenv CLASSPATH .:servlet_hakemisto/servlet.jar:$CLASSPATH

 

ja Windowsissa komennolla

 

set CLASSPATH=.;servlet_hakemisto\servlet.jar;%CLASSPATH%

 

Tämän jälkeen ympäristö on valmis servlettien kääntämiseen. Servletti asennetaan palvelimelle siten, että ensin kopioidaan Java-tiedosto palvelimen servlet-hakemistoon, asetetaan CLASSPATH osoittamaan tähän hakemistoon ja käännetään tiedosto tavukoodiksi javac-komennolla.

6.2.        JSDK:n Servlet Runner

Servlet Runner -ohjelma sijaitsee JSDK:n /bin-hakemistossa. JSDK:n versiosta riippuen ajettava ohjelma on joko servletrunner (v. 2.1) tai runner (v.2.2) ja se voidaan käynnistää komentoriviltä. Itse asiassa Servlet Runner käyttää JSDK:n mukana tulevaa luokkaa sun.servlet.http.HttpServer, jolle se välittää annetut komentoriviparametrit. Taulukossa 3 on esitelty Servlet Runnerin komentorivioptiot.


 

Optio

Selitys

Oletusarvo

-p

Portti, jota Servlet Runner kuuntelee.

8080

-b

Odottavien pyyntöjen maksimimäärä.

50

-m

Yhtäaikaisten yhteyksien maksimimäärä.

100

-t

Yhteyden muodostamiseen kuluvan ajan maksimiarvo, jonka jälkeen yhteys katkaistaan (millisekunteina).

5000

-d

Servlettihakemisto.

<tämän hetkinen hakemisto>/examples

-r

Dokumenttien juurihakemisto.

<tämän hetkinen hakemisto>/examples

-s

Hakemisto, jossa tiedosto servlet.properties sijaitsee.

<tämän hetkinen hakemisto>/examples

-v

Tulostaa informaatiota ohjelman toiminnan aikana.

Pois päältä.

 

Taulukko 3. Servlet Runnerin komentorioptiot.

 

Servletin aliakset ja alustusparametrit määritellään tiedostossa servlet.properties. Parametrit määritellään seuraavassa muodossa:

 

<parametrin_nimi>=<parametrin_arvo>

 

Esimerkiksi servletin alias määritellään seuraavasti:

 

servlet.esimservlet.code=EsimerkkiServlet,

 

jossa esimservlet on alias servletille EsimerkkiServlet.class. Samoin määritellään arvot Servlet Runnerin servletille välittämille alustusparametreille rivillä

 

servlet.esimservlet.initArgs=arvo1=10,\

arvo2=100.

 

Käyttämällä takakauttamerkkiä ’\’ voidaan parametrien luettavuutta parantaa huomattavasti kirjoittamalla seuraava parametri seuraavalle riville.

 

Servlettejä kutsutaan kirjoittamalla selaimen URL:ksi

 

http://localhost:8080/servlet/<servletin_nimi>/[/polku_informaatio][?<query_string>],

 

jossa servletin_nimi on joko servletin alias tai sen varsinainen nimi, esimerkiksi esimservlet tai EsimerkkiServlet. Servletille välitetty polkuinformaatio voidaan selvittää servletissä HttpServletRequest-olion metodilla getPathInfo. query_stringiin voidaan sisällyttää nimi/arvo-pareja, jotka saadaan selville HttpServletRequest-luokan getParameterNames- ja getParameterValues-metodeilla. query_string on muotoa nimi1=arvo1&nimi2=arvo2.

 

Servlet Runner pitää käynnistää joka kerta uudelleen, kun servlettiä muutetaan.

6.3.        Java-servlettejä tukevat WWW-palvelimet

Sunin Java Web Server (JWS) oli ensimmäinen kaupallinen tuote, jossa oli tuki servleteille. JWS:stä on saatavilla ilmainen kokeiluversio Sunin kotisivuilta. Yksinkertaisin tapa asentaa servletit JWS:ään on kopioida servletit palvelimen juurihakemistossa sijaitsevaan hakemistoon /servlet.

 

JWS:ssä servletti voidaan myös rekisteröidä, jolloin se on mahdollista ladata heti JWS:n käynnistyessä tai assosioida servletti johonkin URL:ään, esimerkiksi http://localhost:8080/index.html. Näitä asetuksia voidaan muuttaa käyttämällä hallintasovelmaa, jota käytetään URL:n http://localhost:9090 kautta. Tämä tosin vaatii Java 1.1 -yhteensopivan selaimen.

 

Muita suoraan servlettejä tukevia palvelimia ovat muun muassa Netscapen Enterprise Server sekä Apache Jserv, joka on täysin ilmainen tuote.

6.4.        Servlettikoneet

WWW-palvelin voi tukea servlettejä myös servlettikoneen kautta. Tällaisia ovat esimerkiksi Netscapen Fast Track, Apache Web Server ja Microsoftin Internet Information Server.

 

Yleisimmin käytetty servlettikone on Live Softwaren Jrun. Se tukee edellä mainittuja palvelimia ja se voidaan asentaa myös erilaisiin käyttöjärjestelmiin, kuten Windows, Unix ja Macintosh. Toinen vastaava tuote on New Atlanta Communicationsin ServletExec, joka myös tukee tunnetuimpia palvelimia ja käyttöjärjestelmiä.

7.      Yhteenveto

Java-servletti on tehokas, turvallinen ja käyttöjärjestelmästä riippumaton ohjelmointi-rajapinta. Java-servletti -rajapinnan lisäksi on käytettävissä Javan luokkakirjastot, jotka antavat monipuoliset työkalut kaikenlaiseen ohjelmointiin.

 

Java-servleteissä on myös mahdollisuus asiakaspyyntöjen uudelleensuuntaukseen muilla palvelimilla sijaitseville servleteille. Tämän lisäksi hajautettujen sovelluksien toteutuksessa on käytettävissä Javan oma RMI-tekniikka ja CORBA-luokat.

 

Siten Java-servletti tarjoaa varteenotettavan vaihtoehdon muille menetelmille luoda dynaamisia WWW-sivuja sekä interaktiivisia ja hajautettuja WWW-palveluja.

 

 

 

 

 


Lähteet

 

 

Callaway Dustin R, “Inside Servlets: Server-Side Programming for the Java Platform”, Addison Wesley Longman, Massachusetts, 1999.

 

Campione Mary, Walrath Kathy, Huml Allison and Tutorial Team, “The Java Tutorial Continued: The Rest of the JDK: The Java Series”, Addison Wesley Longman, Massachusetts, 1998.

 

Cornell Gary and Horstmann Cay S., “Core Java 2 Volume 2: Advanced Features”, Prentice Hall, 1999.

 

Magelan Institute, “Fundamentals of Java Servlets”, saatavilla WWW-muodossa <URL: http://developer.java.sun.com/developer/onlineTraining/Servlets/Fundamentals/index.html >, Java Developer Connection, luettu 30.8.2000.