Java Servlets Σταύρος Πολυβίου Εισαγωγή Ένας από τους πρώτους τρόπους δηµιουργίας δυναµικών σελίδων είναι το Common Gateway Interface (CGI). Ο web server καλεί προγράµµατα τα οποία εκτελούνται στον εξυπηρετητή και τα οποία επιστρέφουν HTML κώδικα για αποστολή και παρουσίαση στο φυλλοµετρητή ενός πελάτη. Μειονεκτήµατα: Τα προγράµµατα είναι εκτελέσιµα αρχεία για µία συγκεκριµένη πλατφόρµα (επεξεργαστή + λειτουργικό σύστηµα). Για κάθε request (= πελάτη) δηµιουργείται ξεχωριστό process, κάτι το οποίο επηρεάζει δυσµενώς την κλιµακοθετησιµότητα (scalability). 1
Τι είναι τα Java Servlets; Η απάντηση της Java στο CGI. Ένα servlet είναι µία κλάση της Java η οποία χρησιµοποιείται για την επέκταση των δυνατοτήτων ενός εξυπηρετητή ο οποίος φιλοξενεί εφαρµογές που βασίζονται στο µοντέλο αιτήµατος ανταπόκρισης (request-response). Η πιό συχνή τους εφαρµογή είναι στην επέκταση των δυνατοτήτων εξυπηρετητών διαδικτύου (web servers), δηλαδή εξυπηρετητών που βασίζονται στο πρωτόκολλο HTTP. Τα servlets επικοινωνούν µε ένα εξυπηρετητή µέσω ενός container Π.χ. Apache Tomkat για τον Apache Web Server. Επίσης µπορούν αν πακετοποιηθούν σε ένα Web Archive (WAR) αρχείο. Πλεονεκτήµατα servlets έναντι CGI Απόδοση: δε δηµιουργείται ξεχωριστή διεργασία για κάθε αίτηµα πουκαταφθάνει. Το κάθε αίτηµα εξυπηρετείται από ένα νήµα, ενώ το Java Virtual Machine φορτώνεται µόνο µία φορά και παραµένει στη µνήµη. Ευχρηστία: είναι γραµµένα σε Java, µία εύχρηστη και ευρέως διαδεδοµένη γλώσσα. Εκφραστικότητα: τα servlets µπορούν να επικοινωνούν τόσο µεταξύ τους, όσο και µε τονweb server. Μπορούν ακόµη να διατηρήσουν πληροφορίες από αίτηµα σεαίτηµα. 2
Πλεονεκτήµατα servlets έναντι CGI (2) Φορητότητα: µέσω του κατάλληλου container και JVM µπορούν να εγκατασταθούν πάνω σε οποιοδήποτε εξυπηρετητή, πάνω σε οποιαδήποτε πλατφόρµα. Χαµηλό κόστος: µε χρήση δωρεάν containers, κατά προτίµηση σε συνδυασµό µε ένα δωρεάν εξυπηρετητή. Ασφάλεια: ένα servlet µπορεί να εκτελεστεί σε ένα αµµοδοχείο (sandbox), προστατεύοντας έτσι τον εξυπηρετητή από τυχόν κακόβουλα ή κακογραµµένα servlets. Πακέτα που µας ενδιαφέρουν javax.servlet: γενικό πακέτο για τη δηµιουργία servlets. javax.servlet.http: πακέτο ειδικά για τη δηµιουργία HTTP servlets. Εµείς θα επικεντρωθούµε στο δεύτερο πακέτο. 3
javax.servlet.servletexception javax.servlet.unavailableexception javax.servlet.http.httpservletrequest javax.servlet.http.httpservletresponse javax.servlet.servletconfig java.lang.exception java.lang.cloneable javax.servlet.servlet java.lang.throwable javax.servlet.http.cookie java.io.serializable java.lang.object javax.servlet.http.httpsession javax.servlet.genericservlet javax.servlet.http.httpservlet java.util.eventobject javax.servlet.http.httpsessioncontext javax.servlet.http.httpsessionbindingevent javax.servlet.http.httpsessionevent Η κλάση HttpServlet Είναι µία αφηρηµένη κλάση (abstract class). Συνεπώς δε µπορούν να δηµιουργηθούν αντικείµενα απόαυτήτηνκλάση. Οι υποκλάσεις που δηµιουργούµε απότοhttpservlet συνήθως επεγράφουν τουλάχιστο µία από τις ακόλουθες µεθόδους: doget: χειρίζεται αιτήµατα HTTP τύπου GET. dopost: χειρίζεται αιτήµατα HTTP τύπου POST. doput: χειρίζεται αιτήµατα HTTP τύπου PUT. dodelete: χειρίζεται αιτήµατα HTTP τύπου DELETE. init, destroy: δέσµευση και απελευθέρωση πόρων. getservletinfo: αυτοπεριγραφή του servlet. 4
Βασικές µέθοδοι της κλάσης HttpServlet public void init(servletconfig config) Καλείται από το container όταν το servlet πρόκειται να τεθεί σε λειτουργία. Κληρονοµείται από την κλάση GenericServlet. public void destroy() Καλείται από το container όταν το servlet πρόκειται να καταστραφεί. Αναλαµβάνει την απελευθέρωση τυχόν πόρων που κατακρατόνται από το servlet. Κληρονοµείται από την κλάση GenericServlet. void service(servletrequest req, ServletResponse res) Αποστέλει το αίτηµα του πελάτη στη προστατευµένη µέθοδο service (βλέπε επόµενη). protected void service(httpservletrequest req, HttpServletResponse resp) Λαµβάνει HTTP αιτήµατα από την προηγούµενη µέθοδο και προωθεί το καθένα στην εκάστοτε µέθοδο που το χειρίζεται (βλέπε επόµενη διαφάνεια). Βασικές µέθοδοι της κλάσης HttpServlet (2) Οι ακόλουθες µέθοδοι χειρίζονται συγκεκριµένους τύπους HTTP αιτηµάτων και έχουν την εξής γενική µορφή protected void doxxx(httpservletrequest req, HttpServletResponse resp) ώπου Xxx ο τύπος του αιτήµατος που χειρίζονται. dodelete: χειρίζεται HTTP αιτήµατα τύπου DELETE. doget: χειρίζεται HTTP αιτήµατα τύπου GET. dohead: χειρίζεται HTTP αιτήµατα τύπου HEAD. dooptions: χειρίζεται HTTP αιτήµατα τύπου OPTIONS. dopost: χειρίζεται HTTP αιτήµατα τύπου POST. doput: χειρίζεται HTTP αιτήµατα τύπου PUT. dotrace: χειρίζεται HTTP αιτήµατα τύπου TRACE. 5
Παρένθεση: σύντοµη εισαγωγή στο HTTP Το HTTP πρωτόκολλο καθορίζει την ακόλουθη δοµή για ένα µήνυµα (αίτηµα ή απάντηση): 1. Μία αρχική γραµµή που τερµατίζεται µε CRLF* 2. Μηδέν ή περισσότερες επικεφαλίδες (header lines), της µορφής Header: Value CRLF 3. Μία κενή γραµµή η οποία τερµατίζεται µε CRLF 4. Ένα προαιρετικό κυρίως µέρος (body) *CRLF: Carriage Return/Line Feed Αρχική γραµµή ενός αιτήµατος (request) Παράδειγµα: GET /path/to/file/index.html HTTP/1.0 Αποτελείται από: 1. Το όνοµα τηςµεθόδου µε κεφαλαία (στο παράδειγµά µας GET). 2. Το URL του πόρου (resource) το οποίο αφορά το αίτηµα. 3. Την έκδοση (version) του HTTP πρωτοκόλλου που χρησιµοποιείται. 6
Αρχική γραµµή µίας απάντησης (response) Παράδειγµα: HTTP/1.0 404 Not Found Αποτελείται από: 1. Την έκδοση (version) του HTTP πρωτοκόλλου που χρησιµοποιείται. 2. Ένα τριψήφιο αριθµό, τον κώδικα κατάστασης (status code). Το πρώτο ψηφίο υποδηλεί τη γενική κατηγορία του µηνύµατος: 1xx πληροφοριακό µήνυµα 2xx επιτυχία 3xx επαναδροµολόγηση σε άλλο URL 4xx σφάλµα τουπελάτη 5xx σφάλµα του εξυπηρετητή 3. Ένα σύντοµο µήνυµα στα Αγγλικά(µη προκαθορισµένο). Επικεφαλίδες (headers) Περιέχουν πληροφορίες για το αίτηµα ή την απάντηση και το αντικείµενο που αποστέλλεται στο κυρίως µέρος του µηνύµατος. Το HTTP 1.0 καθορίζει 16 προαιρετικές επικεφαλίδες. Το HTTP 1.1 καθορίζει 45 προαιρετικές επικεφαλίδες και µία υποχρεωτική (Host). Παραδείγµατα: User-Agent: η εφαρµογή που υποβάλει το αίτηµα. Server: ο εξυπηρετητής που αποστέλει την απάντηση. Last-Modified: χρόνος τελευταίας τροποποίησης του πόρου που επιστρέφεται. Content-Type: MIME τύπος του body (π.χ. text/html, image.gif) Content-Length: µέγεθος body σε bytes. 7
Μέθοδοι HTTP GET: αίτηµα για ανάκτηση πόρου. HEAD: αίτηµα για ανάκτηση των επικεφαλίδων που περιγράφουν τον πόρο µόνο. POST: αποστολή δεδοµένων στον εξυπηρετητή για επεξεργασία. Το HTTP 1.1 καθορίζει επιπλέον τις ακόλουθες µεθόδους (τις οποίες δε θα εξετάσουµε): PUT, DELETE, OPTIONS και TRACE. Hello world µε servlets import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet // Επεγράφουµε τηνdoget όταν δεν περιµένουµε POSTDATA από τον πελάτη. public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException // Καθορίζουµε τοπεριεχόµενο της επικεφαλίδας Content-Type της απάντησης response.setcontenttype("text/html"); PrintWriter out = response.getwriter(); // Για την εγγραφή του body 8
Hello world µε servlets (2) // Εγγραφή του body συνήθως κώδικας HTML. out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " + "Transitional//EN\">\n" + "<HTML>\n" + "<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" + "<BODY>\n" + "<H1>Hello WWW</H1>\n" + "</BODY></HTML> ); Ανάγνωση τιµών µεταβλητών από HTML form import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; public class ReadParams extends HttpServlet public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException response.setcontenttype("text/html"); PrintWriter out = response.getwriter(); 9
Ανάγνωση τιµών µεταβλητών από HTML form (2) // Με τη µέθοδο getparameter διαβάζουµε µία GET ή POST µεταβλητή. out.println( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " + "Transitional//EN\">\n" + "<HTML>\n" + "<HEAD><TITLE>Read parameters</title></head>\n" + "<BODY>\n" + "<H1 ALIGN=CENTER>" + title + "</H1>\n" + "<UL>\n" + " <LI>ID: " + request.getparameter( ID") + "\n" + " <LI>Name: " + request.getparameter( name") + "\n" + " <LI>Surname: " + request.getparameter( surname") + "\n" + "</UL>\n" + "</BODY></HTML>"); Ανάγνωση τιµών µεταβλητών από HTML form (3) public void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException // Καλούµε τον ίδιο κώδικα που χρησιµοποιούµε γιατηµέθοδο GET. // Με αυτό τον τρόπο χειριζόµαστε και τις POST µεταβλητές. doget(request, response); 10
Γενικό παράδειγµα µεταβλητών (απόσπασµα) Όταν δε ξέρουµε ταονόµατα των µεταβλητώνεκτωνπροτέρων. Χειρίζεται και µεταβλητές µε πολλαπλές τιµές. Enumeration paramnames = request.getparameternames(); while(paramnames.hasmoreelements()) String paramname = (String)paramNames.nextElement(); paramvalues = request.getparametervalues(paramname); for (int i=0; i<paramvalues.length; i++) Παράδειγµα ανάγνωσης επικεφαλίδων (απόσπασµα) Enumeration headernames = request.getheadernames(); while(headernames.hasmoreelements()) String headername = (String)headerNames.nextElement(); String headervalue = request.getheader(headername)); 11
ηµιουργία/ανάγνωση cookies public void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException Cookie[] cookies = request.getcookies(); for(int i=0; i<cookies.length; i++) String cookiename = cookies[i].getname(); String cookievalue = cookies[i].getvalue(); Cookie usercookie = new Cookie("user-id", 131977"); response.addcookie(usercookie); ηµιουργία sessions // Ανάκτηση του session στο οποίο ανήκει το request (και συνεπώς // ο πελάτης). Με την παράµετρο true γίνεται αυτόµατη δηµιουργία νέου // session σε περίπτωση που δεν υπήρχε. HttpSession session = request.getsession(true); // Ανάκτηση της αποθηκευµένης κατάστασης ενός αντικειµένου βάσει // του session του request. Η αναζήτηση γίνεται µέσω του bind name. SomeClass previousvalue = (SomeClass)session.getAttribute("previousValue"); if (previousvalue == null) previousvalue = new SomeClass( ); // Προσθήκη αντικειµένου στο παρόν session και ταυτοποίησή του µέσω του // bind name. session.setatrribute("previousvalue", previousvalue); 12
Java Server Pages (JSP) ΗαπάντησητηςJava στο ASP, PHP κτλ. Μεγάλο µέρος µίας δυναµικής σελίδας είναι στατικό. εν υπάρχει λόγος το servlet µας να δηµιουργεί το στατικό µέρος µίας σελίδας µε println. Μία JSP σελίδα περιέχει κανονικό HTML κώδικα για τα στατικά µέρη της σελίδας, και ενσωµατωµένο κώδικα Java γιαταδυναµικά µέρη (όπως και στο PHP). Στην πραγµατικότητα, την πρώτη φορά που θα ζητηθεί µία JSP σελίδα, αυτή µεταγλωττίζεται σε servlet. Σε κατοπινά αιτήµατα λειτουργεί σαν κανονικό servlet. Με τη χρήση tag libraries µπορεί ακόµη νααποφευχθείτο γράψιµο κώδικα Java µέσα στη σελίδα! Τα tags αυτά µεταφράζονται σε κλάσεις που τους αντιστοιχούν. Παράδειγµα JSP κώδικα <%@ page errorpage="myerror.jsp" %> <%@ page import="com.foo.bar" %> <html> <head> <%! int serverinstancevariable = 1;%> </head> <% int localstackbasedvariable = 1; %> <table> <tr> <td> <%= "expanded inline data " + 1 %> </td> </tr> 13
Servlet που προκύπτει package jsp_servlet; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import com.foo.bar; import class _myservlet implements javax.servlet.servlet, javax.servlet.jsp.httpjsppage int serverinstancevariable = 1; Servlet που προκύπτει (2) public void _jspservice( javax.servlet.http.httpservletrequest request, javax.servlet.http.httpservletresponse response ) throws javax.servlet.servletexception, java.io.ioexception javax.servlet.servletconfig config = ; Object page = this; PageContext pagecontext = ; javax.servlet.jsp.jspwriter out = pagecontext.getout(); HttpSession session = request.getsession( true ); try out.print( "<html>\r\n" ); out.print( "<head>\r\n" ); 14
Servlet που προκύπτει (3) int localstackbasedvariable = 1; out.print( "<table>\r\n" ); out.print( " <tr><td>" ); out.print( tostringorblank( "expanded inline data " + 1 ) ); out.print( " </td></tr>\r\n" ); catch ( Exception _exception ) 15