Java wybrane technologie spotkanie nr 4 Serwlety c.d. 1
Wprowadzenie Narzucona struktura katalogów aplikacji (większa przenośności) webapps -app1 -app2 -app3 (root) -*.html, *.gif, *.js, *.css (być może w podkatalogach) -*.jsp -WEB-INF (kontener ma dostęp do tych plików, a klient nie) -classes -pakiet1 -Klasa1.class -lib -archiwum.jar -web.xml Aplikacja może być umieszczona w archiwum war klient nie ma również dostępu do katalogu META-INF 2
Pytanie Jeżeli w skład aplikacji wchodzi applet spakowany w archiwum jar, gdzie należy umieścić to archiwum? 3
Zawartość deskryptora deklaracje poszczególnych serwletów mapowania serwletów klasy używane do obsługi zdarzeń z cyklu życia aplikacji (listenery) parametry dla ServletContext definicje filtrów i mapowania filtrów konfiguracja sesji (timeout) więzy bezpieczeństwa biblioteki tagów lista plików powitalnych odwzorowania typów MIME nazwy JNDI 4
Jeszcze jeden przykładowy desktyptor <?xml version="1.0" encoding="utf-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>serwlety2</display-name> <context-param> <param-name>parametraplikacji</param-name> <param-value>ala ma kota</param-value> </context-param> <servlet> <description>opis</description> <display-name>testdeskryptora</display-name> <servlet-name>testdeskryptora</servlet-name> <servlet-class>wyklad.testdeskryptora</servlet-class> <init-param> <param-name>parametrserwletu</param-name> <param-value>ola ma kota</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>testdeskryptora</servlet-name> <url-pattern>/testdeskryptora</url-pattern> </servlet-mapping> </web-app> 5
zawartość <url-pattern> zaczyna się od / i kończy się na /* *.rozszerzenie / (serwlet domyślny) dokładne dopasowania 6
Odwzorowanie URL na serwlet Host Context Path Servlet Path Path Info http://www.myserver.com /mywebapp /helloservlet /hello Request URI Najpierw dopasowywany jest Context Path, a potem kolejno aż się uda: całe Request URI dokładnie do któregoś servlet mapping (Path Info = null) najdłuższy możliwy prefiks Request URI do któregoś servlet mapping, separatorem jest "/" jeżeli ostatni człon Request URI zawiera rozszerzenie, to (Path Info = null) żądanie jest przekierowywane do domyślnego serwletu lub jeżeli takiego nie ma wyświetlana jest informacja o błędzie 7
Przykład <servlet-mapping> <servlet-name>pathservlet</servlet-name> <url-pattern>/pathservlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>pathservlet</servlet-name> <url-pattern>/trelemorele/*</url-pattern> <!--zaczyna sie od / i konczy na /*--> </servlet-mapping> <servlet-mapping> <servlet-name>pathservlet</servlet-name> <url-pattern>*.fiubzdziu</url-pattern> <!--zaczyna sie od *.--> </servlet-mapping> <servlet-mapping> <servlet-name>pathservlet</servlet-name> <url-pattern>/alamakota</url-pattern> <!--musi dokladnie pasowac--> </servlet-mapping> <!--domyslny servlet dla aplikacji--> <!-- <servlet-mapping> <servlet-name>domyslny</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> --> trelemorele trelemorele.txt trelemorele/ala.txt trelemorele?foo=bar trelemorele/xxx.fiubzdziu ala/ma/kota/ola.fiubzdziu ola.fiubzdziu alamakota kosikosilapki 8
Parametry vs atrybuty po jednej wartości dla danej nazwy parametry: serwlety (ServletConfig), aplikacja (ServletContext) nie można zmieniać programistycznie (tylko odczytywać) String getinitparameter(string name) java.util.enumeration getinitparameternames() atrybuty żądanie (HttpRequest), sesja (HttpSession), aplikacja (ServletContext) Object getattribute(string name) Enumeration getattirbutenames() void setattribute(string name, Object value) void removeattibute(string name) 9
Wzorzec obserwator ServletRequestAttributeListener void attributeadded(servletrequestattributeevent ev) void attributeremoved(servletrequestattributeevent ev) void attributereplaced(servletrequestattributeevent ev) HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener ServletContextAttributeListener void attributeadded(servletcontextattributeevent ev) void attributeremoved(servletcontextattributeevent ev) void attributereplaced(servletcontextattributeevent ev) 10
Listenery c.d. ServletContextListener void contextdestroyed(servletcontextevent ev) void contextinitialized(servletcontextevent ev) public class ContextListenerTest implements ServletContextListener { public void contextinitialized(servletcontextevent ev) { try { Connection c = // tworzymy połączenie z bazą danych ev.getservletcontext().setattribute("con", c); catch (Exception e) { public void contextdestroyed(servletcontextevent ev) { try { Connection c = (Connection) ev.getservletcontext().getattribute("con"); c.close(); catch (Exception e) { 11
Listenery c.d. HttpSessionListener metody analogiczne jak w ServletContextListener HttpServletRequestListener metody analogiczne jak w ServletContextListener 12
Wskazywanie Listenerów Bezpośrednio w web-app <listener> <listener-class>wyklad.contextlistenertest</listener-class> </listener> Wszystkie interfejsy można obsługiwać jedną klasą 13
Klastry Zalety Fail-over support Load balancing Aplikacja odpowiednio przygotowana na istnienie kilku egzemplarzy każdego serwletu składowe statyczne składowe egzemplarza różnice lokalnego systemu plików istnienie kilku egzemplarzy ServletContext 14
Co gwarantuje specyfikacja Każda aplikacja ma tylko jeden egzemplarz ServletContext na danej JVM wyjątkowo ServletContext domyślnej aplikacji jest tylko na jednej JVM atrybuty ServletContext ustawiana na jednej JVM są niewidoczne na pozostałych (można się w zamian wspierać bazą danych) ServletContextEvent i ServletContextAttributeEvent nie muszą być propagowane na inne JVM parametry ServletContext są dostępne na wszystkich JVM Sesja w każdej chwili jest obsługiwana na tylko jednej JVM (może, ale nie musi migrować) jeżeli sesja jest migrowana, to atrybuty (implementujące java.io.serializable, kontener może nie przyjmować innych argumentów - IllegalArgumentException) również o migrowaniu informowane są atrybuty implementujące HttpSessionActivationListener 15
Pytania Aplikacja używa ServletContextListener do powiadamiania administratora, gdy dochodzi do jej wyłączenia. Czy używanie tej aplikacji w środowisku rozproszonym będzie miało wpływ na tą funkcjonalność? Nie. Egzemplarz ServletContext będzie utworzony na każdym serwerze i administratorzy zostaną powiadomieni o zniszczeniu każdego z nich. Aplikacja przechowuje listę aktualnie zalogowanych użytkowników w ServletContext i wypisuję ją na żądanie. Jaki wpływ na tą funkcjonalność będzie miało używanie tej aplikacji w środowisku rozproszonym? Żądania będą informowały o użytkownikach zalogowanych tylko na maszynie obsługującej żądanie (podzbiór wszystkich użytkowników). 16
Sesja Integer i; HttpSession session = request.getsession(); i = (Integer) session.getattribute("wyklad.liczniknasesji.i"); if(i==null) i = new Integer(0); else i++; session.setattribute("wyklad.liczniknasesji.i", i); 17
Współbieżność sekcje krytyczne atrybuty klasy (synchronizujemy się na this) atrybuty z zasięgów (synchronizujemy się np. na session) synchronized (this) { try { Thread.currentThread().sleep(5000); catch (InterruptedException e) { System.err.println("Przerwany"); OneThreadServlet 18
Filtry Co może filtr: na podstawie analizy żądania: kontynuowanie przetwarzania lub samodzielne wygenerowanie odpowiedzi wstępne przetworzenie żądania dodatkowe przetworzenie odpowiedzi przekierowanie żądania do innego zasobu 19
Przykład public class WitajŚwiecieFiltrów implements Filter { private FilterConfig fiterconfig; public void init(filterconfig filterconfig) throws ServletException { this.fiterconfig = filterconfig; //w aplikacjach webowych parametry są w rzeczywistości //HttpServletRequest i HttpServletResponse public void dofilter(servletrequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; res.setcontenttype("text/html;charset=utf8"); PrintWriter pw = res.getwriter(); pw.println("<html>"); pw.println("<head></head>"); pw.println("<body>"); pw.println("<h3>witaj świecie filtrów!</h3>"); pw.println("</body>"); pw.println("</html>"); pw.close(); public void destroy() { 20
Desktyptor <filter> <filter-name>witajswieciefiltrow</filter-name> <filter-class>wyklad.witajświeciefiltrów</filter-class> </filter> mapowanie dla URI <filter-mapping> <filter-name>witajswieciefiltrow</filter-name> <url-pattern>/filtry/*</url-pattern> </filter-mapping> mapowanie dla serwletu <filter-mapping> <filter-name>witajswieciefiltrow</filter-name> <servlet-name>pathservlet</servlet-name> </filter-mapping> od wersji 2.5 można: podać * zamiast nazwy serwletu w jednym servlet-mapping wymienić kilka servlet-name i url-pattern 21
Kolejność filtrów Kolejność: Najpierw są wszystkie filtry dla URI, potem dla serwletu. W ramach każdej grupy kolejność jest zgodna z kolejnością wymienienia w deskryptorze Kolejność filtrowania odpowiedzi jest odwrotna do kolejności filtrowania żądania 22
Interfejsy/klasy związane z filtrowaniem javax.sevlet.filter implementuje programista javax.servlet.filterconfig filtry też mają parametry inicjalizacji String getfiltername() Enumeration getinitparameternames() ServletContext getservletcontext() javax.servlet.filterchain dostarcza kontener void dofilter(servletrequest, ServletResponse) przekazuje sterowanie do następnego filtra lub do zasobu jeżeli ten był ostatni zmianę żądania/odpowiedzi można uzyskać przez opakowanie odpowiadających im obiektów: javax.servlet.servletrequestwrapper javax.servlet.servletresponsewrapper javax.servlet.http.httpservletrequestwrapper javax.servlet.http.httpservletresponsewrapper 23
Przykład import javax.servlet.http.*; public class ZawszeOdpowiadajWrapper extends HttpServletRequestWrapper { public AntyCacheWrapper(HttpServletRequest request) { super(request); @Override public String getheader(string name) { if (name.equals("if-modified-since")) return null; else return super.getheader(name); 24
Przykład c.d. class ByteArrayServletOutputStream extends ServletOutputStream { ByteArrayOutputStream baos; ByteArrayServletOutputStream() { this.baos = new ByteArrayOutputStream(); @Override public void write(int b) throws IOException { baos.write(b); ByteArrayOutputStream tobytearrayoutputstream() { return baos; byte[] tobytearray() { return baos.tobytearray(); 25
Przykład c.d. public class ZamieńNaHtmlWrapper extends HttpServletResponseWrapper { public ZamieńNaHtmlWrapper(HttpServletResponse response) { super(response); private ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); private PrintWriter pw = new PrintWriter(basos.toByteArrayOutputStream()); public PrintWriter getwriter() { return pw; public ServletOutputStream getoutputstream() { return basos; byte[] tobytearray() { return basos.tobytearray(); 26
Przykład c.d. public class TextToHtmlFilter implements Filter { public void dofilter(servletrequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; ZawszeOdpowiadajWrapper zowr = new ZawszeOdpowiadajWrapper(req); ZamieńNaHtmlWrapper znwr = new ZamieńNaHtmlWrapper(res); filterchain.dofilter(zowr, znwr); String prefix = "<html><body background=\"tlo.png\"><pre>"; String suffix = "</pre></body></html>"; StringBuilder sb = new StringBuilder(prefix); String oryginalnydokument = new String(znwr.toByteArray()); sb.append(oryginalnydokument); sb.append(suffix);... res.setcontenttype("text/html;charset=utf8"); res.setcontentlength(sb.length()); PrintWriter pw = res.getwriter(); pw.println(sb.tostring()); 27
Filtry c.d. dla każdego wpisu <filter> będzie jeden egzemplarz filtru na każdej maszynie wirtualnej filtry muszą być przygotowane na współbieżną obsługę wielu żądań można filtrować RequestDispatcher.forward(), RequestDispatcher.include() i strony obsługi błędów <filter-mapping> <filter-name>jakiśfiltr</filter-name> <url-pattern>/*</url-pattern> <dispatcher>request</dispatcher> <dispatcher>forward</dispatcher> <dispatcher>include</dispatcher> <dispatcher>error</dispatcher> </filter-mapping> jak nie ma <dispatcher> to tylko dla żądań 28
Niektóre zastosowania uwierzytelnianie sporządzanie logów i audyt kompresja szyfrowanie przekształcenia XSLT przekierowanie żądania do innego zasobu (przy pomocy RequestDispatcher) MVC z filtrami zamiast serwletów serlwety są dobre jeżeli decyzja jaki widok wyświetlić jest podejmowana na podstawie reguł biznesowych JSP generujące XML i HTML 29