Bevezetés
Ebben a leckében arra fogjuk használni a NetREXX-et, hogy CGI (Common Gateway Interface) programokat írjunk benne. A CGI program tulajdonképpen egy olyan szkript, amely a web kiszolgálón fut és a kliens által küldött paraméterek felhasználásával web oldalakat generál. CGI programokat nagyon sok nyelven írni, s a legtöbb kiszolgáló támogatja a Java programokat is, amelyeket servleteknek is neveznek.
CGI alapok
Nagyon sok esetben van arra szükség, hogy olyan információt tegyünk elérhetôvé a hálózaton, amely eredeti formátuma nem alkalmas arra, hogy a böngészôk közvetlenül értelmezni tudják. Sokszor arra is szükség van, hogy ne az egész információhalmazt, hanem annak csak azt a részét jelenítsük meg, amelyre a felhasználónak szüksége van. Vegyünk például egy sok száz bejegyzést tartalmazó DB2 adatbázist! Ezt nem tudják a böngészôk olvasni, de még ha tudnák is, akkor se érnénk vele sokat, mert a felhasználóknak sok idôbe telne kiválasztani a számunkra hasznos részeket. Az ilyesfajta feladatok megoldására kiválóan alkalmasak a CGI programok, mivel azokkal könnyedén megoldható a felhasználó számára érdekes adatok kiválasztása és annak a böngészô számára értelmezhetô formátumúra alakítása.
A felhasználó böngészôje paramétereket juttathat el a CGI programhoz, hogy ezzel például behatárolja azt, hogy mely adatokat kívánja lekérni az adatbázisból. A paramétereket a böngészô az URL részeként tárolja el, s ezekbôl a szerver oldalon környezeti változók keletkeznek, amelyeket aztán a CGI program le tud kérdezni a futás során. Mivel a Java programok nem férnek hozzá közvetlenül a környezeti változókhoz, ezért a kiszolgálókat úgy módosították, hogy azok a környezeti változókat automatikusan átkonvertálják rendszertulajdonságokká (system properties), amelyek nevei megegyeznek a környezeti változók neveivel és így lekérdezhetôk a System.getProperty metódussal. A négy leggyakrabban használt környezeti változót és azok rendeltetését összefoglaltuk az alábbi táblázatban:
| Környezeti változó: | Rendeltetés: |
| SCRIPT_NAME | A CGI program neve |
| REMOTE_ADDR | A kliens TCP/IP címe |
| QUERY_STRING | A paramétereket tartalmazó sztring a Get HTML metódus használata esetén |
| CONTENT_LENGTH | A bemenetre küldött bájtok száma a Post HTML metódus esetén |
A négy környezeti változó közül a QUERY_STRING a legfontosabb, mivel ez tartalmazza az átadandó paraméterek nevét és azok értékét. A paramétereket a & jel választja el egymástól. A QUERY_STRING tartalmának általános alakja a következô:
változó1=érték1&változó2=érték2&változó3=érték3...
Fontos még azt is tudnunk, hogy a speciális karaktereket a % jel és a speciális karakter kódja írja le. A % karaktert például %25-tel helyettesíti a böngészô, míg a szóközöket + jelekre cseréli.
A paraméterek átadásának másik módja a CGI szkript standard bemenetére küldött adatok feldolgozása. Ilyenkor a HTML nyelv Post metódusát használjuk. A bemenetre küldött adatokat a következô sorral lehet kiolvasni:
query = BufferedReader(InputStreamReader(System.in)).readLine()
A paraméterek feldolgozása és a CGI programban definiált egyéb mûveletek elvégzése után a kapott eredményeket a standard kimenetre írjuk ki, amelyet aztán a web kiszolgáló továbbít a klienshez. Ez azt jelenti, hogy egy NetREXX programban a Say utasítás segítségével írathatjuk ki a visszaadandó HTML oldal sorait. Mielôtt azonban az elsô sort kiíratnánk, küldenünk kell egy, a fájl adattípusát leíró sort, amelyet mindig egy üres sornak kell követnie:
Content-Type: text/html <html> <=== a HTML oldal kezdete ... <=== a HTML oldal törzse </html> <=== a HTML oldal vége
Példaprogramok
Reméljük, hogy még mindenki emlékszik a 8. leckében használt DB2 adatbázisra. Ebbôl az adatbázisból fognak ugyanis CGI programjaink adatokat kikeresni és a web kiszolgáló közremûködésével elküldeni a kliensnek. Az elsô program a minta adatbázis EMPLOYEE táblázatából keresi ki a felhasználó által megadott nevû munkavállalókat. A második program kilistázza egy adott munkavállaló összes adatát. Az elsô példaprogramhoz mellékeltünk egy HTML oldalt is (lecke10a.htm), amely a következôképpen jelenik meg egy böngészôben:
![[ntrx1001]](ntrx1001.gif)
A keresett személy nevének begépelése és a Submit Query gombra bökés után a böngészô elküldi a kiszolgálónak a keresett nevet, amelyet az aztán továbbpasszol az alábbi CGI programnak:
/* lecke10a.nrx */
import java.sql.
Class lecke10a
properties static
prefix = Rexx "USERID" -- a keresett oszlop azonosítója
con = Connection -- DB2 kapcsolat
driver = String 'COM.ibm.db2.jdbc.net.DB2Driver'
/* driver = String 'COM.ibm.db2.jdbc.app.DB2Driver' */
url = String 'jdbc:db2:/loopback:8888/sample'
/* url = String 'jdbc:db2:sample' */
partialname = String
/* fômetódus */
method main(args=String[]) static
args = args
say "Content-Type: text/html" -- a kötelezô sor
say "" -- ez is kötelezô
say "<html>" -- HTML fájl kezdete
say "<head><title>Információ az alkalmazottakról</title></head>"
say "<body>"
say "<H2>Találati lista</H2>"
say "<br> Program: "System.getProperty("SCRIPT_NAME")
say "<br> Client : "System.getProperty("REMOTE_ADDR")
list = Rexx System.getProperty("QUERY_STRING") -- paraméterek
list = queryTranslate(list)
list = list.translate("%", "*")
say "<br> Query :" list
parse list "name=" partialnamex " " -- a név kiolvasása
partialname = partialnamex.upper'%'
jdbcConnect() -- JDBC kapcsolódás az adatbázishoz
performRetrieve() -- DB2 SQL
say "</body></html>" -- a HTML vége
return
/* a kapott paramétersztring átalakítása */
method queryTranslate(qry=Rexx) private static returns Rexx
qryt = qry.translate(" ", "+") -- + = szóköz!
ist = qryt.pos("%")
loop while ist > 0
c = qryt.substr(ist+1,2).x2c
qryt = qryt.substr(1,ist-1)""c""qryt.substr(ist+3)
ist = qryt.pos("%", ist+1)
end
return qryt
/* kapcsolódás az adatbázishoz */
method jdbcConnect() private static
do
say "<br>Kapcsolat :" url
Class.forName(driver)
con = Connection DriverManager.getConnection(url, 'userid', 'password')
if con.getWarnings() \= null then do
say "<p> Error "con.getWarnings().getMessage()
return
end
dma = DatabaseMetaData con.getMetaData()
say '<br>Driver : 'dma.getDriverName() dma.getDriverVersion()
catch ex=SQLException
say '<p> *** SQLException történt ***'
say "<br> "ex.getMessage()
loop while (ex \= null)
say '<br>SQLState: 'ex.getSQLState()
say '<br>Message: 'ex.getMessage()
say '<br>Vendor: 'ex.getErrorCode()
ex = ex.getNextException()
say '<br>'
end
catch ex2=java.lang.Exception
say "<p> Error: "ex2.getMessage()
ex2.printStackTrace()
end
return
/* az adatok letöltése */
method performRetrieve() private static
say "<p> Az alkalmazottak letöltése folyik: "partialname
do
query = 'SELECT empno, lastname, firstnme' -
'FROM' prefix'.employee' -
'WHERE lastname LIKE "'partialname'"'
say "<p>"
say "<table border=2 cellpadding=0>"
say "<tr>"
say "<th>Sorszám</th> <th>Vezetéknév</th> <th>Keresztnév</th>"
say "<tr>"
stmt = Statement con.createStatement()
rs = ResultSet stmt.executeQuery(query)
more = boolean rs.next()
loop row=0 by 1 while (more)
num = Rexx rs.getString("empno")
say "<td>" "<a href='lecke10b.class?number="num"'><b>"num"</b></a></td>"
say "<td>" rs.getString("lastname")"</td>"
say "<td>" rs.getString("firstnme")"</td>"
say "<tr>"
more = rs.next()
end
say "</table>"
rs.close()
stmt.close()
say "<p> Találtunk "row" alkalmazottat."
catch ex=SQLException
say "<p> Error: "ex.getMessage()
end
return
A példaprogram a kötelezô típusleíró sor és az azt követô üres sor kiíratásával kezdôdik. Ezután lekérdezzük a környezeti változókat a már említett system properties metódus segítségével. A paramétereket tartalmazó környezeti változó tartalmát átalakítjuk a queryTranslate metódussal, amely visszanyeri a speciális karaktereket és a szóközöket. Az adatok birtokában kapcsolódunk az adatbázishoz a jdbcConnect metódussal, és a performRetrieve metódusban egy SQL parancs segítségével lekérdezzük azon alkalmazottak listáját, amelyek neve megegyezik a felhasználó által megadott névvel. Az így kapott adatokat HTML táblázatban íratjuk ki a standard kimenetre, amelyet aztán a web kiszolgáló juttat el a felhasználó böngészôjéhez. Valami ehhez hasonló oldalnak kell megjelennie:
![[ntrx1002]](ntrx1002.gif)
Amint látjuk, a CGI programot a P* paraméterrel hívtuk meg, ami válaszul az összes P-vel kezdôdô nevû alkalmazottat megjelenítette. A program ugyanis képes a * karakter gyorsítókarakterként történô értelmezésére is. A megtalált alkalmazottak sorszáma egyben egy link is, amelyre klikkantva elindul a második CGI példaprogram, amely megjeleníti az adott sorszámú alkalmazott adatait:
/* lecke10b.nrx */
import java.sql.
Class lecke10b
properties static
prefix = Rexx "USERID" -- oszlopazonosító
con = Connection -- DB2 kapcsolat
driver = String 'COM.ibm.db2.jdbc.net.DB2Driver'
--driver = String 'COM.ibm.db2.jdbc.app.DB2Driver'
url = String 'jdbc:db2:/loopback:8888/sample'
--url = String 'jdbc:db2:sample'
empno = String
/* fômetódus */
method main(args=String[]) static
args = args
say "Content-Type: text/html" -- a kötelezô sorok
say ""
say "<html>" -- kezdôdik a HTML fájl
say "<head><title>Információ az alkalmazottakról</title></head>"
say "<body>"
say "<H2>Az alkalmazott adatai</H2>"
say "<br> Program: "System.getProperty("SCRIPT_NAME")
list = System.getProperty("QUERY_STRING") -- paraméterek
parse list "number="empno " "
jdbcConnect() -- JDBC kapcsolódás
performRetrieve() -- DB2 SQL
say "</body>" -- a HTML vége
say "</html>"
return
/* kapcsolódás az adatbázishoz */
method jdbcConnect() private static
do
Class.forName(driver)
con = Connection DriverManager.getConnection(url, 'userid', 'password')
if con.getWarnings() \= null then do
say "<p> JDBC driver: "driver
say "<br>Kapcsolat : "url
say "<p> Error "con.getWarnings().getMessage()
return
end
catch ex=SQLException
say '<p> *** SQLException történt ***'
say "<br>" ex.getMessage()
loop while (ex \= null)
say '<br>SQLState: 'ex.getSQLState()
say '<br>Message: 'ex.getMessage()
say '<br>Vendor: 'ex.getErrorCode()
ex = ex.getNextException()
say '<br>'
end
catch ex2=java.lang.Exception
say "<p> Error : "ex2.getMessage()
ex2.printStackTrace()
end
return
/* az adatok lekérése */
method performRetrieve() private static
say "<br> Az adatok letöltése folyik: "empno
do
query = 'SELECT empno, firstnme, midinit, lastname,' -
'phoneno, sex, birthdate, hiredate, job, edlevel,' -
'salary, bonus, comm, workdept' -
'FROM' prefix' .employee' -
'WHERE empno = " 'empno' " '
stmt = Statement con.createStatement()
rs = ResultSet stmt.executeQuery(query)
more = boolean rs.next()
say "<pre>"
loop row=0 by 1 while (more)
say "<p>"
say "Sorszám : "rs.getString("empno")
say "Név : "rs.getString("firstnme") rs.getString("midinit") rs.getString("lastname")
say "Telefon : "rs.getString("phoneno")
say "Nem : "rs.getString("sex")
say "Született: "rs.getString("birthdate")
say "Felvétel : "rs.getString("hiredate")
say "Funkció : "rs.getString("job")
say "Képzetts.: "rs.getString("edlevel")
say "Fizetés : "rs.getString("salary")
say "Bonus : "rs.getString("bonus")
say "Extra : "rs.getString("comm")
if rs.getString("workdept") \= null then
say "Részleg : "rs.getString("workdept")
more = rs.next()
end
say "</pre>"
rs.close()
stmt.close()
if row=0 then say "<p> A(z) "empno". sorszámú alkalmazott nem létezik."
catch ex=SQLException
say "<p> Error: "ex.getMessage()
end
return
A második program nagyon hasonlít az elsôhöz. Most is a kötelezôen elküldendô sorokat íratjuk ki elôször, majd pedig lekérdezzük a környezeti változókban rejlô bemeneti információkat. Az adatok birtokában kapcsolódunk az adatbázishoz és lekérdezzük, majd pedig megjelenítjük a megadott sorszámú alkalmazott adatait.
![[ntrx1003]](ntrx1003.gif)
Leckénk elején említettük, hogy ha a HTML oldalon Post metódust használnak, akkor a paraméterek a CGI program standard bemenetére kerülnek. Ebben az esetben természetesen nem mûködnek a fenti példaprogramok, mivel a QUERY_STRING környezeti változóból nem lehet kinyerni a szükséges információt. Viszonylag kis munkával át lehet azonban alakítani a programokat, hogy alkalmasak legyenek a Post metódus támogatására. Az alábbi programrészlet azt mutatja be, hogy hogyan lehet átalakítani a második példaprogramot:
method main(args=String[]) static
args = args
say "Content-Type: text/html"
say " "
say "<html>"
say "<head><title>Információ az alkalmazottakról</title></head>"
say "<body>"
say "<H2>Az alkalmazott adatai</H2>"
say "<br> Program: "System.getProperty("SCRIPT_NAME") -
"query length" System.getProperty("CONTENT_LENGTH")
-- itt történik a standard input kiolvasása
list = BufferedReader(InputStreamReader(System.in)).readLine()
parse list "number=" empno " "
jdbcConnect()
performRetrieve()
say "</body>"
say "</html>"
return
REXX GYÍK:
K1. Mely web kiszolgálók alkalmasak servletek futtatására?
V1. Szinte mindegyik korszerû web kiszolgáló alkalmas a Java vagy NetREXX nyelven írt CGI programok futtatására. Néhány példa a sok közül: Apache, MS IIS, Lotus Domino, Netscape.
K2. Igaz-e, hogy a CGI programok használata nem biztonságos?
V2. Mivel a CGI programok a kiszolgálón futnak és az átadott paramétereket a böngészô által megadott URL tartalmazza, ezért elképzelhetô, hogy egy furfangosan megszerkesztett URL-lel olyan mûvelet hajtható végre, amellyel a kiszolgáló tönkretehetô. A fenti példaprogramok esetében pl. megadható olyan URL, amellyel az adtabázis törlését lehet kierôszakolni, amennyiben a CGI program rendelkezik a megfelelô jogosultságokkal. A problémák elkerülése érdekében végrehajtás elôtt tesztelni kell a megadott paramétereket és csak annyi jogosultságot kapjon a CGI program futtatója, amennyi a helyes mûködéshez feltétlenül szükséges.
Gyakorlat:
1. Írja át az elsô példaprogramot oly módon, hogy az mind a Get, mind pedig a Post metódust támogassa!
| Kádár Zsolt 2000. 07. 28. | [ Elôzô lecke | Következô lecke | Tartalom ] |