Serwlety
Transkrypt
Serwlety
Serwlety Co to jest serwlet? kawałek kodu w Javie po stronie serwera HTTP rozszerza moŜliwośći serwera CGI, w Javie, wzbogacone o biblioteki ułatwiające Ŝycie programiście (np. utrzymywanie sesji, wpólne zasoby serwletów, ciasteczka, obsługa GET/POST/PUT) coś jak aplet: róŜni się miejscem wykonywania oraz dostępnymi bibliotekami serwlety mogą korzystać ze standardowych klas Javy (z VM), klas wchodzących w skład Servlet API: javax.servlet.* i javax.servlet.http.* oraz ewentualnie z dodatkowych bibliotek zainstalowanych na serwerze. serwlety nie mają interfejsu uŜytkownika, komunikują się z przeglądarką (za pośrednictwem serwera WWW wspierającego serwlety) za pomocą protokołu HTTP • • • • • • Przykładowy kod serwletu import import import import import import java.io.IOException; java.io.PrintWriter; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; public class SimpleServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out; response.setContentType("text/html"); out = response.getWriter(); out.println("<HTML><HEAD><TITLE>"); out.println("Hello world servlet"); out.println("</TITLE></HEAD><BODY>"); out.println("<H1>" + title + "</H1>"); out.println("<P>This is output from SimpleServlet."); out.println("</BODY></HTML>"); out.close(); } } Po co są serwlety? Zasadniczo: do wyświetlania dynamicznych stron WWW. • • • • przetwarzanie formularzy forwarding wyświetlania wyników zapytań do b. d. (tu zaleca się strony JSP) są częścią J2EE Miejsce serwletów w aplikacjach J2EE • • • • • • serwlety to rozszerzenie serwera WWW blisko interfejsu uŜytkownika: zorientowane na Ŝądanie/odpowiedź (request/response) serwlety działają w odpowiedzi na Ŝadania klienta są interfejsem do serwera aplikacyjnego/bazy danych/EJB pełnią rolę kontrolera w schemacie Model-View-Controller, za widok odpowiedzialne są strony JSP (czyli kod Javy imieszczony w pliku html), model to EJB w serwletach zaleca się umieszczanie tylko lekkich funkji biznesowych, ale w małych aplikacjach cała logika moŜe być w serwletach i JSP Rysunek 1. Warstwy w modelu J2EE (obrazek z J2EE Tutorial) Rysynek 2. Kontenery w modelu J2EE (obrazek z J2EE Tutorial) Komunikacja z klientem • • • • przedefiniowanie metod doGet, doPost, doPut, doDelete. Te metody są wywoływane przez metodę service zdefiniowaną w Servlet przy kaŜdym Ŝądaniu do serwera dotyczącym serwletu. service to tablica rozdzielcza. dane od klienta (przeglądarki) są w obiekcie HttpServletRequest. WaŜne metody obiektu request: o getParameter - zwraca parametr, np. z formularza o getCookies - daje ciasteczka o getSession - podaje obiekt sesji o getReader (dla danych tekstowych) o getInputStream (dla danych binarnych) dane do klienta symbolizuje obiekt HttpServletResponse WaŜne metody obiektu response: o getWriter (dane tekstowe) o getOutputStream (dane binarne) szczegóły i pozostałe metody: javadoc do javax.servlet.http.* Przykład komunikacji z klientem <html> <head><title>Formularz</title></head> <body> <form action="/karolb/test" method="post"> Podaj tekst: <input type="text" name="tekst" value=""> </form> </body> </html> zadziała następujący serwlet: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class TestServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out; String tekst; response.setContentType("text/html"); out = response.getWriter(); out.println("<HTML><HEAD><TITLE>Wyniki z formularza"); out.println("</TITLE></HEAD><BODY>"); tekst = request.getParameter("tekst"); if (tekst != null) out.println("Z formularza otrzymalem: " + tekst); else out.println("Brak danych z formularza"); out.println("</BODY></HTML>"); out.close(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out; String tekst; response.setContentType("text/html"); out = response.getWriter(); out.println("<HTML><HEAD><TITLE>Uwaga</TITLE>"); out.println("</HEAD><BODY>"); out.println("Ten serwlet nie obsluguje metody GET!"); out.println("</BODY></HTML>"); out.close(); } } Wielowątkowość Serwlety wykonują się w środowisku wielowątkowym, więc moŜe się zdarzyć, Ŝe wiele kopii serwletu będzie działać naraz. A moŜe być tak, Ŝe będzie jedna instancja serwletu, ale wiele wątków wykonujących metodę service. JeŜeli w serwlecie jest odwołanie do zasobu, który wymaga wyłączności to: • • trzeba zapewnić wykluczanie ręcznie, np. uŜywając zmiennych/metod synchronized zadeklarować, Ŝe serwlet implementuje interfejs SingleThreadModel, wtedy serwer uruchomi jednocześnie tylko jedną instancję metody service, np. public class ReceiptServlet extends HttpServlet implements SingleThreadModel. Ten interfejs nie zawiera Ŝadnych metod, jest tylko znacznikiem dla kontenera. Cykl Ŝycia serwletu • • na początku system woła metodę init(), w niej moŜna np. połączyć się z bazą danych o jeŜeli coś pójdzie nie tak to naleŜy rzucić wyjątek UnavailableException o moŜna wołać metodę getInitParameter, interpretowaną przez serwer (kontener), np. jako init-param z pliku web.xml w Tomcat'cie. W takim parametrze moŜe być zapisany np. URL do bazy danych. gdy serwer zdecyduje wyrzucić serwlet z pamięci to wywołuje metodę destroy. Uwaga! JeŜeli metoda do[Get|Post] wykonuje się długo, to trzeba zadbać o jej zakończenie. JeŜeli serwlet uruchamiał wątki, to teŜ trzeba je przerwać.. Szczegóły w Servlet Tutorial na stronach Sun'a. Utrzymywanie stanu (session tracking) To jedna z najwaŜniejszych zalet serwletów. Nie ma tego w CGI. Schemat: • • • • HttpSession session = request.getSession(true); (czytanie/pisanie) Klasa ref_obiektu1 = new Klasa(); (pisanie) session.putAttribute("dluga.nazwa_atr", ref_obiektu1); (pisanie) Klasa ref_obiektu2 = (Klasa) session.getAttribute("dluga.nazwa_atr"); (czytanie) Cechy: • • • Wkładanie i wyjmowanie obiektów z sesji nie ma nic wspólnego z interfejsem serializable. Obiekty nie są nigdzie składowane, tylko istnieją w pamięci kontenera. Metody operują na referencjach. Po odczytaniu referencji metodą getAttribute operujemy juŜ na oryginalnym (jedynym!) obiekcie. Po odczytaniu atrybutu trzeba sprawdzić czy jest równy null, jeŜeli tak to trzeba utworzyć nowy obiekt (uwaga na wielowątkowość). Sesje działają standardowo na ciasteczkach. W ciasteczku jest tylko id sesji, reszta siedzi w pamięci kontenera. JeŜeli przeglądarka nie obsługuje ciasteczek to trzeba uŜywać metody encodeURL(). Gdy serwlet ma wyświetlić link do drugiego serwletu to w metodzie doGet() trzeba umieścić: String wartosc = "abc"; out.println("Link do drugiego serwletu z parametrem: " + ... "<a href=\"" + response.encodeURL("/drugi_serwlet?param=" + wartosc) + "\">" + ...); • • Takie wywołanie dorzuci do parametrów w adresie identyfikator sesji. Sesje nie są związane z jednym serwletem, róŜne serwlety korzystają z tej samej sesji Przykład uŜycia sesji • Wkładanie i wyjmowanie z sesji import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class DodajProdukt extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(true); Koszyk koszyk = (Koszyk) session.getAttribute("pl.zakupy.koszyk"); if (koszyk == null) { koszyk = new Koszyk(); session.setAttribute("pl.zakupy.koszyk", koszyk); } koszyk.dodaj(request.getParameter("produkt")); RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/wyswietl_koszyk.jsp"); if (dispatcher != null) dispatcher.forward(request, response); } } • • Uwaga: po modyfikacji koszyka nie trzeba go specjalnie zapisywać do sesji. W skrypcie /wyswietl_koszyk.jsp wyświetlam zawartość koszyka. Ciasteczka (Cookies) Schemat uŜycia: • Wysyłanie String wartosc = "byle-co"; Cookie ciacho = new Cookie("Nazwa", wartosc); response.addCookie(ciacho); Odbieranie • Cookie[] cookies = request.getCookies(); String wartosc; for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals("Nazwa")) wartosc = thisCookie.getValue(); } Lepiej jednak uŜywać sesji. Dzielenie zasobów między serwlety (ServletContext) Schemat (podobny do tego przy obsłudze sesji): ServletContext context = getServletContext(); (czytanie/pisanie) Klasa ref_obiektu1 = new Klasa(); (pisanie) context.setAttribute("dluga.nazwa_atr", ref_obiektu1); (pisanie) Klasa ref_obiektu2 = context.getAttribute("dluga.nazwa_atr"); (czytanie) • • • • Przechowywanie jest podobne do tego związanego z sesjami, ale te same obiekty są dostępne dla wszystkich serwletów na serwerze (w kontenerze). Dlatego trzeba szczególnie uwaŜać na nazwy atrybutów, Ŝeby się nie zdarzyło, Ŝe dwie róŜne apliacje chcą korzystać z tego samego atrybutu. Przykład uŜycia kontekstu public class WyswietlDane extends HttpServlet { public synchronized void init() throws ServletExcpetion { BazaDanych bd = (BazaDanych) getServletContext().getAttribute("pl.sklep.bd.baza"); if (bd == null) { bd = new BazaDanych("http://pulsar", "scott", "tiger"); getServletContext().setAttribute("pl.sklep.bd.baza", bd); } } ... } Inne moŜliwości serwletów • • ServletDispatcher - umoŜliwia przekierowywanie do innego serwletu (metoda forward, patrz wyŜej) i włączanie wyjścia innego serwletu we własne wyjście (metoda include). filtry - klasa Filter. Filtrów uŜywa się do modyfikowania komunikatów między klientem a docelowym serwletem na serwerze. Filtry moŜna łączyć w łańcuchy. Przykładowe zastosowania filtrów to: autentykacja, szyfrowanie, kompresja. Szczegóły w J2EE Tutorial i JavaDocu. Przykład z J2EE Tutorial: public final class HitCounterFilter implements Filter { private FilterConfig filterConfig = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) return; StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); Counter counter = (Counter)filterConfig. getServletContext(). getAttribute("hitCounter"); writer.println(); writer.println("==============="); writer.println("The number of hits is: " + counter.incCounter()); writer.println("==============="); // Log the resulting string writer.flush(); filterConfig.getServletContext(). log(sw.getBuffer().toString()); ... chain.doFilter(request, wrapper); ... } } Instalowanie serwletów na Tomcat'cie • • Najpierw trzeba zainstalować Tomcata. Tomcat to serwer http napisany w Javie przez Apache Software Group. MoŜe działać jako samodzielny serwer (domyślnie na porcie 8080) lub zintegrować się z Apachem. Kompilacja serwletu: javac -classpath $TOMCAT_HOME/lib/servlet.jar Test.java • Plik Test.class ma trafić do katalogu $TOMCAT_HOME/webapps/test_kat/WEB- • Trzeba utworzyć plik $TOMCAT_HOME/webapps/test_kat/WEB-INF/web.xml, o treści: INF/classes <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <servlet> <servlet-name> test_serw </servlet-name> <servlet-class> Test </servlet-class> </servlet> <servlet-mapping> <servlet-name> test_serw </servlet-name> <url-pattern> /test </url-pattern> </servlet-mapping> </web-app> • • Jak widać najpierw nazwa serwletu (test_serw) jest wiązana z klasą (Test.class), a potem nazwa serwletu jest przypisywana do ścieŜki (w obrębie kontekstu). Do pliku $TOMCAT_HOME/conf/server.xml trzeba dopisać: <Context path="/test_kat" docBase="webapps/test_kat" reloadable="true"/> • • wewnątrz znacznika <ContextManager>. W tym pliku określa się jak z zewnątrz adresować dany kontekst, tu kontekst z katalogu webapps/test_kat będzie widziany jako katalog test_kat. Teraz moŜna do serwletu odwoływać się poprzez localhost:8080/test_kat/test.