Andmebaasid SQL, ODBC, Servlet, veebirakendus Enamik programme talletab andmeid kusagil. Üheks levinumaks väljundiks programmi poolt vaadates on failid kettal, teiseks andmebaasid. Failide eeliseks on kohene kasutamisvalmidus. Baasiga suhtlemise puhul peab lisaks oma rakendusele ka andmebaasiga suhtlemist võimaldav vahend masinas leiduma. Peaaegu hädavajalikuks aga muutub andmebaas mitmelõimelise programmi korral, sest ise korralikke lukustusmehhanisme kirjutada võib olla päris aeganõudev ettevõtmine. Samuti aitavad andmebaaside päringuvahendid andmete keerukama ülesehituse korral sobivaid väärtusi üles leida. Andmemudeleid ja päringukeeli leidub mitte. 2004. aastal ja sellele eelnenenud paaril aastakümnel on aga valitsevaks relatsioonilised, tabelitest koosnevad andmebaasid ning baasidega suhtlemiseks SQL-keel. Andmebaasiühenduse loomine Et andmeid ja tabeleid saaks kuhugi baasi paigutada, selleks peab kõigepealt andmebaas loodud olema. Mõnes paigas saab seda teha vaid administraatoriõigustes inimene, Windowsi masinates võib aga sageli igaüks omale kasutamiseks-katsetamiseks andmebaasiserveri püsti panna ning sinna nii palju baase luua kui kettamaht võimaldab. Enesele MySQLi vahendid kopeerida saab http://www.mysql.com/-i alt. Vaja läheb nii andmebaasikeskkonda ennast kui draiverit sellega ühendumiseks. Toimingute tegemiseks peab kõigepealt baasiserveri käima lükkama. Käsuks mysqld-shareware ning selle tulemusena hakkab server kuulama väratit 3306, kui pole määratud teisiti. Käsk mysqladmin lubab luua ja kaotada baase ning teha muudki korraldusega seonduvat. Siin luuakse baas nimega pood. Kui aga soovida lihtsamalt läbi ajada, siis ei pea selleks mitte oma serverit püsti panema. Võib rahulikult toime tulla olemasolevate Accessi, Exceli või suisa tekstifaili draiveritega. Kui aga MySQL installeeritud, siis saab seda kasutada. Küllaltki universaalne koht andmebaasidele ligi pääsemiseks on ControlPanel'i alt avanev ODBC. Et MySQLile sealtkaudu ligi pääseks, on vaja installeerida vastav draiver, näiteks Connector/ODBC, mis vabalt kättesaadava MySQLi kodulehelt. Sealt ControlPanel'i alt on näha, millised ressursid juba kasutada on, samuti annab siit oma baasile ODBC ühendus luua, mille abil siis kergesti võib olemasolevate draiverite abil programmide kaudu sinna andmeid saatma ja sealt pärima hakata. Nimetatud vahelüli (Open DataBase Connectivity) on lihtsalt ühine protokoll, mille kaudu saavad suhelda osapooled, kes üksteise keelt ei tunne (pole otseühenduseks vastavaid draivereid). Uue andmeallika loomiseks tuleb vajutada Add... ning pakutakse toetatavatest tüüpidest välja, millist kasutaja soovib luua. Kui siin näites soovime ühenduda MySQL-i baasiga, tuleb ka vastavat tüüpi draiver valida. Draiveri juures tuleb määrata parameetrid. Vähemasti nimi, mille alt Windows'is vastavat andmeallikat tuntakse ning milline on tegelik baasi nimi serveri Kui kogu loomine läks õnnelikult, siis jõuame tagasi algse lehe juurde, kuhu on tekkinud ka vastloodud ühendus, siin näites nime all poebaas. Edasi pole muud, kui asuda loodud ühendust kasutama. Baasi sisse võib tabeleid ja andmeid lisada mitut moodi. Accessi või Exceli puhul saab avada vastava programmi ning rahumeeli tähed ja numbrid tabelisse kirjutada. MySQLil oma kliendi kaudu saab ka baasi külge ühenduda ning seal SQL lausete abil soovitud muutusi tekitada. Kui tahta edaspidi panna oma programm baasi andmeid kasutama, siis on paslik alustada lühemast käsureast, mis parajasti baasi sisse ühe üheveerulise tabeli loob ning sinna sisse väärtuse paigutab. Võib ette kujutada, et tabelis on kirjas, mitu palli parajasti poe laos hoiul on. Andmebaasiga suhtlemiseks tuleb kõigepealt mällu laadida draiver. ODBC tarvis on Java starndardkomplektis kaasas sun.jdbc.odbc.JdbcOdbcDriver. Luuakse ühendus, jättes kasutajanime ja parooli koht tühjaks, kuna meie katsebaasi puhul neid ei nõuta. Saadetakse käsklaused teele ning suletakse ühendus. import java.sql.*; public class Baasilooja1{ public static void main(String argumendid[]) throws Exception{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection cn= DriverManager.getConnection( "jdbc:odbc:poebaas", "", ""); Statement st=cn.createStatement(); String lause="CREATE TABLE pallid (kogus int);"; st.executeUpdate(lause); lause="INSERT INTO pallid (kogus) values ('0');"; st.executeUpdate(lause); cn.close(); } } Kui programmi tekst sisse kirjutatud, siis enne käivitamist tuleb see kompileerida ning seejärel käima lasta. Näidet vaadates paistab tulemus tühjavõitu olema. Kompileerimisel ei tulnud veateadet seetõttu, et ühtki viga ei leitud. Käivitamisel pole midagi näha, kuna kogu tegevus käis programmi ja baasi vahel ning polnud küsitud, et midagi ekraanile näidataks. Kui väljatrükilauseid vahele pikkida, eks siis oleks ka käivitajal rohkem midagi vaadata olnud. Et töö siiski päris tühi ei olnud ja midagi ka toimus, sellest annab teada järgmine pilt. Kui ühenduda MySQLi kliendiga baasi taha ning uurida, mis seal sees paikneb, siis on näha, et tekkinud on tabel nimega pallid ning sinna sisse on koguseks pandud 0. Otsene draiver ODBC võimaldab omavahel suhelda paljudel programmidel ning protokollidel, kuid selle puuduste juurde kuulub, et tegemist on veel ühe järjekordse vahelüliga, mis enesele ressursse nõuab ning nagu pudelikael ikka ei pruugi see mitte kõiki häid omadusi läbi lasta, mis kummalgi osapoolel olemas võivad olla. Sellepärast, kui on tegemist tohutute andmehulkade või suurte kiirustega, on mõistlik otsida programmi ja andmebaasi vahele otsest draiverit. Javat ning MySQLi ühendava vahendi leiab näiteks lehelt http://www.mysql.com/downloads/api-jdbc.html Kui sealne arhiiv maha laadida ning lahti pakkida, tekkis kataloog, milles nii draiver ise kui hulga õpetusi, kuidas temaga ümber käia. Kirjadest selgus, et muuta polegi vaja muud kui draiveri nime ning ühenduse URLi. Nüüd saab kirjutada otse jdbc:mysql: . import java.sql.*; public class Pallibaas2{ public static void main(String argumendid[]) throws Exception{ Class.forName("org.gjt.mm.mysql.Driver"); Connection cn=DriverManager.getConnection( "jdbc:mysql:/localhost/pood", "", ""); Statement st=cn.createStatement(); String lause="SELECT kogus FROM pallid;"; ResultSet rs=st.executeQuery(lause); rs.next(); System.out.println("Baasis on "+ rs.getInt("kogus")+" palli"); cn.close(); } } Kui programm panna tööle draiveri kodukataloogis, siis leitakse ise kõik sobivad klassid üles, sest nad on seal lihtsalt käe-jala juures. Soovides aga kohaleveetud draiverit kusagil mujal kasutada, selleks tuleb draiveri klassid arhiividena kaasa võtta ning käivitamisel -classpath abil öelda, millistest arhiividest draiveri osad kokku korjata tuleb. Servlet Baasis paiknevaid andmeid võib vaja olla mitmele poole välja anda. Veebi kaudu on hea andmeid lugeda ning sinna saatmiseks sobivad servletid ehk pisiprogrammikesed veebiserveris. Nagu varsti näha, võime soovi korral oma arvutisse HTTP-serveri püsti panna ning servletid andmebaasi andmeid lugema saata. Alustada võiks aga lihtsa servleti loomisest ja käivitamisest. Ja seletusest, et millega tegu. Java programme käivitatakse päris mitmesugustes paikades. Algseks ja "õigeks" käivitamiskohaks võidakse pidada ju main-meetodit, kuid võimalikke Java-programmide käivituskohti on tunduvalt enam. Rakendid veebilehtedel saavad käiturilt teateid sündmuste kohta ning toimivad vastavalt nendele. Rakendusserveris paiknevad EJB-nimelised komponendid ootavad aga hoopis teistsuguste käskude käivitamist. Ning miniseadmetes toimivad J2ME programmid ärkavad jälle omamoodi. Servlettide praegusaja levinumaks kasutusalaks on veebilehtede väljastamine - kuid mitte ainult. Mitmesugused masinatevahelised teated ning teenused töötavad samuti servlettide kaudu. Kui kord loodud mugav ning suhteliselt turvaline ja kontrollitav võimalus teisest masinast andmete küsimiseks, siis võib seda ju kasutada. Sestap võibki näha servlettide päises kahe paketi importimist: javax.servlet ning javax.servlet.http. Viimane siis HTTP-vahenditega lähemalt seotud klasside tarbeks. Sarnaselt rakendile võetakse ka servlettide puhul aluseks ülemklass ning asutakse selle meetodeid üle katma. Vaid toimingud on rakendi või mobiiliprogrammiga võrreldes teistsugused, ülekatmine ikka samasugune. Erisuseks veel, et servleti puhul iga lehe avamine piirdub funktsiooni ühekordse väljakutsega. Rakendi puhul võivad start, stop ning paint korduvalt käivituda. HTTP-päringute puhul on võimalike toiminguid vähemasti kuus, kuid servlettide puhul levinumateks GET ning POST, mõlemal juhul väljastatakse üldjuhul veebileht. GET-päringu puhul antakse parameetrid kaasa URLi real, nende pikkus on piiratum ning loodud leht võidakse kergemini puhverdada. Tüüpiline kasutusvaldkond on näiteks otsingumootorite juures, kus sama päringu tulemus minutite ja tundide jooksul oluliselt ei muutu. Kui tegemist andmete sisestamisega - näiteks enese võrgu kaudu registreerimisega, siis tuleb paratamatult programm igal korral uuesti käima panna ning selleks kasutatakse POST-nimelist meetodit. Tegemise ajal katsetada on aga GET-i puhul mugavam, sest siis paistavad saadetavad andmed välja. GET-meetodi käivitamiseks tuleb üle katta servleti meetod doGet. Meetodile antud esimese parameetri kaudu saab andmeid päringu kohta: milliselt aadressilt ja masinast tuldi, millised andmed kasutaja kaasa saatis. Teine parameeter tüübist HttpServletResponse võimaldab määrata loodava lehe sisu ning päised. Järgnevalt näha võimalikult lihtne tervitav servlet. import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class Servlet1 extends HttpServlet{ public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException{ PrintWriter valja=vastus.getWriter(); valja.println("Tervist!"); } } Enne tulemuse nägemist tuleb veel pingutada servletile sobiva keskkonna loomise nimel. Kel juba sobiv käivitusserver eelnevalt püsti, sel võib piisata faili kompileerimisest ning sobivasse kataloogi paigutamisest. Kel aga mitte, siis tuleb veidi installeerimisega pead vaevata. 2004. aastal tundub levinud servletikäituriks olevat näiteks Apache Tomcati nimeline veebiserver http://jakarta.apache.org/tomcat/. Sealt allalaetud faili lahtipakkimisel või käivitamisel saab õnnelike juhuste kokkulangemisel tööle oma masinas veebiserveri. Täpsemaid seadistamise juhiseid leiab näiteks aadressilt http://www.coreservlets.com/Apache-Tomcat-Tutorial/. Loodud koodi kompileerimiseks peavad kättesaadavad olema servlettide alusklassid. Need leiab Tomcati installeerimiskataloogi alamkataloogist common\lib\ . Tomcati 4. versiooni puhul on failiks servlet.jar, viienda versiooni puhul servlet-api.jar. Neid võib kättesaadavaks teha CLASSPATH-nimelise muutuja kaudu. Teiseks võimaluseks on aga kopeerida nimetatud fail java interpretaatori laienduste kataloogi, milleks siinses masinas on näiteks C:\j2sdk1.4.2_01\jre\lib\ext, mujal siis vastavalt Java installeerimise asukohale. Edasi võib koodi kompileerida nagu tavalist Java faili. Üheks mugavaks käivitamise kohaks on asukoht Tomcati enese näidete juures nt. C:\Program Files\Apache Group\Tomcat 4.1\webapps\examples\WEB-INF\classes , kuid konfiguratsioonifailide abil saab siin paljutki sättida. Kaasatulnud näited saab käivitada aadressireal examples/servlet-kataloogi kaudu. Mõningase nikerdamise tulemusena võib aga servletid ka juurkataloogis oleva servlet-kataloogi all tööle lükata. Sisestus Kui soovida programmilt vastuseid omapoolsetele andmetele, siis tuleb need kuidagi ka programmile ette anda. Servlettide puhul sisestab kasutaja enamasti andmed veebilehel paiknevasse tekstivälja või muusse sisestuskomponenti. Andmete sisestusnupule vajutamisel jõuavad need paremeetritena järgmisena avatava veebilehte loova programmi kasutusse ning edasi tuleb juba seal otsustada, mida saadud andmetega peale hakatakse. Igal andmeid edastaval sisestuskomponendil sõltumata tüübist on nimi, järnevas näites näiteks "eesnimi". Andmeid vastuvõttev programm saab selle nime järgi küsida just konkreetse elemendi väärtust. Siin pole eraldi määratud, kuhu faili andmeid saata. Sel juhul käivitatakse uuesti sama fail ning uue ringi peal jõuavad eelmisel korral sisestatud andmed kohale. import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Sisestus1 extends HttpServlet { public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException { vastus.setContentType("text/html"); PrintWriter valja = vastus.getWriter(); valja.println(""+ "Sisestus\n"+ "
"+ "
"); valja.println("Tere, "+ kysimus.getParameter("eesnimi")); valja.println(""); } } Esimesel korral aga pole veel andmeid kusagilt võtta ning kysimus.getParameter annab vastuseks tühiväärtuse null. Kui nüüd tekstivälja sisse nimi kirjutada ning sisestusklahvile vajutada, siis võib järgmisel ringil näha, et nimi jõudis avanevale lehele kohale. Lehe keskel ilutseb rõõmsasti "Tere, Juku". Kui tähelepanelikumalt piiluda, siis võib märgata, et programmi nime taga aadressireal paikneb küsimärk ning selle järel sisestatud parameetri nimi ja võrdusmärgi taga väärtus. Sealtkaudu on liikuvad andmed programmeerijale ilusti näha ning tal võimalus kontrollida, mis ja millise nime all serverisse saadeti. Esmakordselt näidatavast tühiväärtusest on täiesti võimalik hoiduda. Selleks tuleb enne nime välja trükkimist kontrollida, kas ikka midagi teele saadeti. Andmed loeti eesnime-nimelisse muutujasse kahel põhjusel. Lühema nimega muutujaga on lihtsalt kergem ümber käia kui pidevalt parameetrit käskluse kaudu küsides. Samuti võib juhtuda, et kui kord mõni parameeter Request-i käest küsitud, siis võidakse arvata, et selle väärtus juba programmeerijal teada on ning seda rohkem enam algses kohas ei säilitata. Tugevamalt tuleb sellise kadumisvõimalusega arvestada andmebaaside juures mõne draiveri ja seadistuse puhul, kuid ka siin on tegemist sama nähtusega. import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Sisestus2 extends HttpServlet { public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException { vastus.setContentType("text/html"); PrintWriter valja = vastus.getWriter(); valja.println(""+ "Sisestus\n"+ "
Sisesta eesnimi: "+ ""+ "
"); String eesnimi=kysimus.getParameter("eesnimi"); if(eesnimi!=null){ valja.println("Tere, "+eesnimi); } valja.println(""); } } Kui nüüd kontrollitakse enne väljatrükki tühiväärtust, siis võib näha, et lehe vastuseosa on ilusti tühi ning mõttetut teksti välja ei kirjutata. Viisakalt sisse kirjutatud nime puhul aga tervitatakse sama rõõmsasti vastu. Räägitakse, et veebilehtede koostamisel tuleb arvestada igasuguste turvaprobleemidega. Ning et üheks märgatavaks ohuks on kasutajate nii kogemata kui meelega sisestatud erisümbolid. Lihtsamatel juhtudel võidakse kirjutada HTML-i kujunduskäsklusi oma teksti ilmestamiseks. Tulemus võib sellisel juhul päris meediv olla. Samas ei takista miski ka Javaskripti koodilõike teksti sisse kirjutamast ning nende tööga ei pruugi kasutaja enam rahul olla. Näiteks praegusel juhul avatakse teateaken. Kui selliseid akent avavaid teateid aga mõnda külalisraamatusse hulgem saab, siis võib lehekülje avamine päris vaevaliseks osutuda. Suuremaks probleemiks siinjuures on, et lehel toimetav Javaskript võib ka näiteks kasutaja sisestatud andmed oma kontrolli alla saada ning hoopis võõrasse serverisse teele saata, mille üle kasutaja sugugi rõõmus ei pruugi olla. Samuti võib juhtuda, et üksik valesse kohta sisestatud < või "-märk tekitab seilris sedavõrra segadust, et järgnev tekst jääb sootuks näitamata või muutub keerulisema paigutusega lehel pilt ees segaseks. Kui kasutaja sisestatud erisümbolid HTML-i reeglitele vastavalt kodeerida, siis pääseb eelpool kirjeldatud muredest. Et kodeerimist läheb vaja siinsest näitest tunduvalt rohkemates paikades, siis sai abifunktsioon paigutatud omaette klassi. Staatilise funktsiooni saab kättesaadava klassi kaudu kohe käima tõmmata, ilma et peaks objekti loomisele jõudu kulutama. Tähtede asendamiseks on kasutatud StringBuffer-tüüpi objekti, kuna puhvrile liitmine on tunduvalt odavam tegevus kui sõnede liitmine. Eriti juhul, kui tekstid kipuvad pikemaks minema. Sest kord valmis loodud Stringi enam muuta ei saa. Tähe lisamiseks tuleb uus mälupiirkond leida ning algsed andmed sinna üle kopeerida. StringBuffer on aga siinkirjeldatud toiminguteks just loodud. public class Abi{ /** * Etteantud tekstis asendatakse HTML-i erisümbolid * lehele sobivate kombinatsioonidega. */ public static String filtreeriHTML(String tekst){ if(tekst==null){return null;} StringBuffer puhver=new StringBuffer(); for(int i=0; i': puhver.append(">"); break; case '&': puhver.append("&"); break; case '"': puhver.append("""); break; default: puhver.append(c); } } return puhver.toString(); } } Andmete filtreerimiseks piisab järgnevast käsust. String eesnimi=Abi.filtreeriHTML(kysimus.getParameter("eesnimi")); Kui klass Abi asub käivituva servletiga samas kataloogis, siis leitakse klass üles. import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Sisestus3 extends HttpServlet { public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException { vastus.setContentType("text/html"); PrintWriter valja = vastus.getWriter(); valja.println(""+ "Sisestus\n"+ "
Sisesta eesnimi: "+ ""+ "
"); String eesnimi=Abi.filtreeriHTML(kysimus.getParameter("eesnimi")); if(eesnimi!=null){ valja.println("Tere, "+eesnimi); } valja.println(""); } } Nüüd võib kasutaja ka proovida skripti sisestada. Tulemusena aga asendatase tekst ära ning lehele jõuab sarnane pilt kui kasutaja kirjutatugi. Kui tahta programmi muundamistööd täpsemalt piiluda, siis tulemuse leiab lehe lähtekoodi vaadates. Sisestus
Sisesta eesnimi:
Tere, <script>alert("kuku")</script> Enamasti sisestatakse lehele rohkem kui üks väärtus. Kui ühe teksti puhul piisas tekstiväljast ning sisestusklahvile vajutades läksid andmed teele, siis rohkemate saadetavate andmete puhul on lisaks vaja ka sisestusnuppu. Selleks siis sisestusväli tüübiga "submit". import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Sisestus4 extends HttpServlet { public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException { vastus.setContentType("text/html"); PrintWriter valja = vastus.getWriter(); valja.println(""+ "Sisestus\n"+ "
"+ "Eesnimi: \n"+ "Perekonnanimi: \n"+ ""+ "
"); String eesnimi=Abi.filtreeriHTML(kysimus.getParameter("eesnimi")); String perenimi=Abi.filtreeriHTML(kysimus.getParameter("perenimi")); if(eesnimi!=null){ valja.println("Tere, "+eesnimi+" "+perenimi); } valja.println(""); } } Nii võib väärtusi sisestada loodud tekstiväljadesse ning pärast rakenduse töö tulemust imetleda. Pilt servleti väljundina Arvutis liikuvaid andmeid võib enamikul juhul käsitleda baidijadana ning ka servlet pole selle poolest erand. Nõnda võib servleti panna väljastama ka pilti või heli. HTTP-ühenduse päiseridadega antakse teada, millist tüüpi andmeid saadetakse ning edasine on juba vastuvõtja ülesanne. Et Javas leiduvad vahendid pildi kirjutamiseks voogu, siis saab andmeid võrdselt õnnelikult saata nii faili kui võrku. Ning siinses näites jõuavadki andmed üle võrgu kasutajani. Pilt luuakse mälus valmis ning lõpuks saadetakse andmed voogu pidi teele. Mugavaks pildi loomise vahendiks on BufferedImage ning sealt küsitud graafiline kontekst. Joonistamine toimub sarnaste käskude puhul nagu mujalgi. import javax.servlet.*; import javax.servlet.http.*; import com.sun.image.codec.jpeg.*; //kuulub SUNi JDK-sse import java.awt.image.*; import java.awt.*; import java.io.*; public class piltservlet2 extends HttpServlet{ public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException{ int suurus=(int)(20+Math.random()*60); BufferedImage pilt=new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); Graphics2D piltg=pilt.createGraphics(); piltg.setColor(Color.red); piltg.fillOval(50-suurus/2, 50-suurus/2, suurus, suurus); vastus.setContentType("image/jpeg"); JPEGCodec.createJPEGEncoder(vastus.getOutputStream()).encode(pilt); } } Ning kui tulemus valmis, võib seda imetleda Servlet ja andmebaas. Veebist saabuvaid ja küsitavaid andmeid on küllalt mõistlik talletada andmebaasis. Sellisel juhul ei pea programmeerija liialt palju pead vaevama andmete poole üheaegsest pöördumisest tekkivate murede üle, sest selle eest hoolitsemine on juba andmebaasimootoritesse sisse ehitatud. Üldjuhul käib andmebaasiühenduse loomine servleti puhul nii nagu mujalgi programmis. Vaja laadida draiver, luua ühendus. Statement-objekt lause edastamiseks ning ResultSet andmete lugemiseks. Kuna ResultSet arvestab, et päringu tulemusel väljastatakse tabel, siis tuleb andmeid ka nõnda välja lugeda. import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.sql.*; public class pallibaas1 extends HttpServlet{ public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException{ vastus.setContentType("text/plain"); PrintWriter valja=new PrintWriter( vastus.getWriter()); try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection cn=DriverManager.getConnection( "jdbc:odbc:poebaas", "", ""); Statement st=cn.createStatement(); String lause="SELECT kogus FROM pallid;"; ResultSet rs=st.executeQuery(lause); rs.next(); valja.println( "Laos on "+rs.getInt("kogus")+" palli."); }catch(Exception viga){ valja.println("Probleem andmebaasiga: " +viga); } } } JSP Servletid on mugavad olukordades, kus lehtedel on staatilist teksti vähe ning enamik sisust tuleb kokku arvutada. Suuremate püsivate tekstide puhul on võimalik neid teistest failidest või andmebaasikirjetest sisse lugeda. Siin lisatakse rakenduse juurkataloogis (nt. webapps/examples) paiknev fail SISU.JSP. import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class kaasamiskatse extends HttpServlet{ public void doGet(HttpServletRequest kysimus, HttpServletResponse vastus) throws IOException, ServletException{ PrintWriter valja=vastus.getWriter(); ServletContext kontekst=getServletConfig().getServletContext(); RequestDispatcher rd=kontekst.getRequestDispatcher("/SISU.JSP"); rd.include(kysimus, vastus); } } Kui aga väljaarvutamist nõudvaid paiku lehel suhteliselt vähem ning märkimisväärse osa lehe loomisest moodustab kujundus, siis sobib kasutada JSP-nimelist võimalust. Siin moodustab lehe põhiosa otse väljastatav tekst ning vaid erisümbolite vahel paiknevates lõikudes käivitatakse programm. Lihtsamal juhul kirjutatakse JSP-kood <% ja %> vahele, kuid uuema standardi järgi võimaldatakse ja soovitatakse JSP-lehed kirjutada XML-standardile vastaval kujul, kus jsp:ga algavad elemendid siis juhivad koodi käivitamist. Järgnevalt on tegemist võimalikult lihtsa näitega, kus lihtsalt arvutatase kokku kahe arvu summa. Tervitus Parim hinne koolis on <%=3+2 %> Käivitamiseks tuleb failid panna samasse kataloogi kuhu harilikud html-failidik, nagu järgnevalt jooniselt näha. Ning veebist vaatamiseks piisab sobiva aadressi sissetoksimisest. JSP-lehti eraldi kompileerida pole vaja, selle eest hoolitseb juba käitur. Sestap ka lehe aeglane avamine esimesel algsel või muutmisjärgsel käivitamisel, sest seal tuleb kogu kompileerimise töö ära teha. Selline lähenemine võib eriti mugav olla näiteks inimestele, kel varem pole kompileerimisega kogemusi olnud ning PHP või muid skripte kirjutades juba harjunud, et piisabki vaid koodi kirjutamisest, kui juba võibki tulemusi imetlema asuda. Et sisimas aga muudetakse JSP lehed enne servlettideks ja alles siis kompileeritakse/käivitatakse, kipuvad saabuvad veateated küllalt arusaamatud ja vähemasti algul häirivad olema. Näiteks võib juhtuda, et liitmisel sattus kogemata üks x-täht arvu taha. Tervitus Parim hinne koolis on <%=3+2x %> Tegemist on ju küllalt lihtsa ja sageli esineva veaga, mis võiks õnnestuda ilma suuremate muredeta ära parandada. Kui aga nüüd lehte avama asuda, ilmneb päris põhjalik veateade: org.apache.jasper.JasperException: Unable to compile class for JSP An error occurred at line: -1 in the jsp file: null Generated servlet error: [javac] Since fork is true, ignoring compiler setting. [javac] Compiling 1 source file [javac] Since fork is true, ignoring compiler setting. [javac] C:\Program Files\Apache Group\Tomcat 4.1\work\Standalone\localhost\examples\arvutus_jsp.java:47: ')' expected [javac] out.print(3+2x ); [javac] ^ [javac] 1 error at org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:130) at org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:293) at org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:353) at org.apache.jasper.compiler.Compiler.compile(Compiler.java:370) at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:473) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:190) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295) ... lisaks veel paarkümmend rida näitamaks millised funktsioonid kust välja kutsuti. Ning seda kõike vaid ühe puuduva tähe pärast. Mõningase piilumise peale leiab koha, mis servletis vigaseks osutus. [javac] C:\Program Files\Apache Group\Tomcat 4.1\work\Standalone\localhost\examples\arvutus_jsp.java:47: ')' expected [javac] out.print(3+2x ); Ning sealt pealt õnnestub aimata, et <%= kujul antud avaldis muudetakse print-käsu sisuks ning sinna taoline uitama asunud x ei sobi. Kui x eemaldada ning leht uuesti laadida, töötab ta jälle. Möödunud veateatest loeb välja ka näiteks loodava servleti asukoha: Tomcati alamkataloog work. Kui muu ei aita, tuleb asuda vastava servleti koodi lähemalt uurima. Aga enamasti ikka õnnestub JSP lehel soovitud kohti muutes ja välja kommenteerides segased kohad kindlaks teha ja parandada. Tsükkel JSP lehe sisse saab Java koodi täiesti rahumeeles kirjutada. Et lõppkokkuvõttes muudetakse JSP-leht ikkagi servletiks, siis käivitamisel polegi nende vahel kuigivõrd vahet. Vaid kasutaja mugavuse mõttes kannatab JSP-lehel tavalist teksti kergemini edasi anda. Nõnda töötab harilik tsükkel Arvude ruudud ühest kahekümneni on:

<% for(int i=1; i<=20; i++){ %> <%= i*i+"
" %> <% } %> Ja tulemus lehel nagu oodatud. Nagu mujal, nii ka siin soovitakse kasutaja käest andmeid saada, muidu poleks ju põhjust lasta programmil lehte kokku panna. Andmed nagu ikka kirjutatakse vormi ning selleks sobib täiesti tavaline HTML-leht. Vormi action-atribuudiga määratakse koht, kuhu sisestatud andmed saadetakse. Nime sisestamine Palun nimi:

Et tekstivälja nimeks oli "nimi", siis võib selle kaudu ka andmed kinni püüda. Nime lugemine Tere, <%= request.getParameter("nimi") %> Ning nagu pildilt paistab, saabuvad vaikimisi GET-päringu korral andmed URL-rea kaudu. Teate kaasamine Kui samu andmeid soovitakse mitmel veebilehel kasutada, siis on mugav andmed ühte kohta kirja panna ning sobivates paikades faili sisse lugeda. Nagu järgnevast näitest paistab, on selliseks käsuks include, nii nagu mõnes muuski veebikirjutuskeeles (PHP, ASP). Faili sisu kaasamine Failis on teade: <%@ include file="teade.txt" %> Ning vastav fail peab lihtsalt samas kataloogis omaette kättesaadav olema. Tere, kool Nõnda võibki töö tulemust imetleda. Enam kasutatakse taolist kaasamist olukordades, kus soovitakse mitmele lehele luua ühesugune päis. Kommentaarid Enamikes keeltes jäetakse programmeerijale võimalus omi märkusi koodi juurde lisada ilma, et tavakasutaja sellest aimu saaks. Olgu siis tegemist rakenduse tutvustamise, koodi üksikute lõikude seletamise või ebasoovitavate lõikude ajutise eemaldamisega. JSP puhul on selleks kolm märgatavalt erinevat võimalust. Esiteks võis HTMLi koodi sisse kirjutada oma "nähtamatu" tekst käskude vahele. Selline tekst jõuab küll kasutaja masinasse, kuid ei ole lehe tavalisel vaatamisel nähtav. Kui soovida tervet JSP-lõiku eemaldada, siis võis selle panna <%-- ja --%> vahele. Nõnda saab lõike kergesti sisse ja välja lülitada. Ning lõppeks kehtivad ka tavalised Java-kommentaarid: // ühe rea tarvis ning /* ja */ pikema lõigu jaoks. Kommentaarid <%-- varjatud kommentaar --%> <% //kommentaar koodi sees %> Uba JSP lehtede sisu soovitatakse võimalikult lihtsaks jätta. Siis on neid võimelised kujundama ka programmeerimiskauged inimesed. Äriloogika ehk arvutused ning andmetega seotud toimingud saab paigutada eraldi ubadeks nimetatud klassidesse ning sealt siis sobivate käskude abil teenuseid küsida. Klassid paigutatakse sinna kuhu servletidki, ainult et soovitavalt veel iga teemaga seotud oad omaette kataloogi ehk paketti. Siinsel paketi nimeks on pandud k1, ning alt pildilt paistab ta ilusti classes-kataloogi alamkataloog olema. Siinse oa ülesandeks on vaid nime meeles pidamine. Ning vaikimisi nimeks on Triin. package k1; import java.io.Serializable; public class Uba1 implements Serializable{ String nimi="Triin"; public void paneNimi(String nimi1){ nimi=nimi1; } public String annaNimi(){ return nimi; } public String tutvusta(){ return "Mu nimi on "+nimi; } } Kui klass loodud, tuleb see kompileerda nagu enamikele muudelegi Java-programmidele kohane. Kompileerimisel peaks aktiivne kataloog olema WEB-INF\classes, nii et kompileerimisel tuleb pakkettide tee mööda katalooge ette anda. Ning kompileeritud class-fail paigutatakse java-failiga samasse kataloogi. C:\Program Files\Apache Group\Tomcat 4.1\webapps\examples\WEB-INF\classes>javac k1\Uba1.java Kui uba valmis, võib teda kasutama hakata. Oa kirjeldamiseks lehel käsklus jsp:useBean. Atribuudiga annan oale nime, mille järgi hiljem selle poole pöörduda. Edasi saan osalt kasutada uba nagu tavalist muutujat. Kuigi - ubade tarbeks on JSP sisse ka mitmesuguseid muid pöördumisvõimalusi leitud. Oa kasutamine <%=nimehoidja.tutvusta() %> <%nimehoidja.paneNimi("Katrin"); %>

Oa sees on nüüd <%=nimehoidja.annaNimi() %> Kui algselt oli oa sees Triin ning eraldi käsuga määrati nimeks Katrin, siis nõnda võib tulemust ka veebilehelt näha. Vaikimisi on oa poole võimalik pöörduda vaid sama lehe avamise jooksul. Kui soovida aga andmeid pikemks talletada, võib määrata skoobiks sessiooni. Nõnda püsivad andmed paigal sama kasutaja mitme järjestikuse pöördumise ajal ning seal võib meeles pidada näiteks teadet, et kasutaja on end juba sisse meldinud. Oa kasutamine <%=nimehoidja.tutvusta() %> <%nimehoidja.paneNimi("Katrin"); %>

Oa sees on nüüd <%=nimehoidja.annaNimi() %> Siin töötab leht esimese pöördumise puhul nii nagu eelmisel korral: kuna midagi pole veel eraldi salvestatud, siis alguses teatatakse ikka vaikimisi nimeks olev Triin. Kui nüüd aga järgmisel korral sama sessiooni jooksul minna oast andmeid küsima, siis on seal kirjas juba eelmisest korrast meelde jäänud nimi. Uba ja andmebaas Et igasugused programmeerimiskäsud püütakse JSP-lehest eemal hoida, siis on mõistlik ka andmebaasiga seotud toimingud oa sisse peita. Kui peitmine korralik, siis ei pruugi JSP-lehe looja sageli teadagi, kus baasis andmeid hoitakse. See võimaldab vajadusel andmekandjat küllalt kergesti vahetada ning vajadusel näiteks andmebaasi sootuks tavalise tekstifailiga asendada. Et ühendamine libedalt läheks, loome ka siin ODBC alla andmeallika. Edasi koostame oa, millel oskused nii andmetabeli loomiseks kui väärtuse seadmiseks ja küsimiseks. Ning kui eelmise oaga võrrelda, siis näevad nad küllalt sarnased välja - ikka käsud väärtuste seadmiseks ja küsimiseks. Ning oa kasutaja ei peagi teadma, et andmeid just baasis hoitakse. package k1; import k1.*; import java.sql.*; public class Baasiuba1{ Connection cn; Statement st; public Baasiuba1(){ try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); cn=DriverManager.getConnection("jdbc:odbc:baas1", "", ""); st=cn.createStatement(); }catch(Exception e){ System.out.println(e); } } public void looBaas() throws SQLException{ String lause="CREATE TABLE pallid (kogus int);"; st.executeUpdate(lause); lause="INSERT INTO pallid (kogus) values ('0');"; st.executeUpdate(lause); } public void setPalliarv(int arv) throws SQLException{ String lause="UPDATE pallid SET kogus="+arv+";"; st.executeUpdate(lause); } public int getPalliarv() throws SQLException{ String lause="SELECT kogus FROM pallid;"; ResultSet rs=st.executeQuery(lause); rs.next(); return rs.getInt("kogus"); } } Kui paketi sees olev uba loodud, tuleb ta kompileerida nagu Java-fail ikka. C:\Program Files\Apache Group\Tomcat 4.1\webapps\examples\WEB-INF\classes>javac k1\Baasiuba1.java Et pallilao administreerimine mugavamalt läheks, selleks on ka tabeli loomiseks omaette "administraatorileht" tehtud. Võrgust leitavate rakenduste puhul võib sageli kohata juhendit, kus üles seadmiseks tuleb vaid andmebaasi nimi määrata või sobiva nimega baas luua ning edasi õnnestub kõik veebi kaudu paika sättida. Siin vaid öeldakse oale, et looBaas (mille juures praegu küll vaid üks tabel luuakse) ning võibki asuda juba rakenduse teeneid kasutama. Kontrolliks küsitakse välja baasis leiduvate palllide arv. Nagu näha, ei tehta seda mitte tavalise funktsiooniväljakutsega, vaid oa väärtuste küsimiseks sobib element jsp:getProperty. Mis küll toimimiseks eeldab, et oal oleks vastavanimeline get-liitega algav meetod. <% pallibaas.looBaas(); %> Baas loodud

Baas loodud

Baas pallide arvu loomiseks õnnelikult loodud. Laos on palli. Nii võib koodilõigu tööd veebist imetleda ning pärast ka andmebaasifailist piiluma minna, et soovitud tabel ka tegelikult loodud ning väärtus sinna sisse pistetud on. Andmete lisamisel küsitakse kasutaja käest lisatavate pallide arvu ning saadetakse tulemused edasi lehele lisamine.jsp. Pallide lisamine

Pallide lisamine

Mitu palli lisatakse lattu?
Too leht saab väärtused URL-i rea pealt kätte ning määrab baas uue pallide arvu. Pärastine väärtuse küsimine nagu ennegi - getProperty kaudu. Pallide lisamine

Pallide lisamine

<% int olemas=pallibaas.getPalliarv(); int juurde=Integer.parseInt(request.getParameter("arv")); int kokku=olemas+juurde; out.println("Laos oli palle "+olemas+" lisatakse "+juurde+"."); pallibaas.setPalliarv(kokku); %> Laos on nüüd palli. Ja võibki koodi tööd veebilehel imetleda. Lisada kannatab ka olemasolevatele juurde. Ning andmebaasi tabelisse vaatama minnes võib veenduda, et sinna ka arv 17 jõudnud on Ostmise puhul on toiming lihtsalt teistpidine. Algul tasub ikka küsida, kas laost üldse midagi võtta on ning siis teada anda, mitut palli osta soovitakse. Pallide ostmine

Pallide ostmine

Laos on palli. Mitu palli ostetakse?
Müümisel kõigepealt kontrollitakse, et nõnda palju kaupa ikka jagub ning vaid sobivuse korral võetakse tehing ette. Pallide eemaldamine

Pallide eemaldamine

<% int olemas=pallibaas.getPalliarv(); int maha=Integer.parseInt(request.getParameter("arv")); int tulemus=olemas-maha; out.println("Laos oli palle "+olemas+" väljastada soovitakse "+maha); if(tulemus<0){ out.println("Väljastada saab vaid "+olemas+" palli"); tulemus=0; } pallibaas.setPalliarv(tulemus); %> Lattu jäi palli. Ning andmetabelist võib taas järele kontrollida, et veebi väljastatud andmed ikka õiged on.