VÝVOJ PODNIKOVÝCH APLIKACÍ NA PLATFORMĚ JAVA - CVIČENÍ Zbyněk Šlajchrt http://java.vse.cz/4it447/HomePage Část 4.
Program Diskuse nad domácím úkolem Refaktoring e-mail klienta Podle vzoru Model-View-Contoller Práce s posluchači Využití posluchače HttpSessionAttributeListener k inicializaci vlastností modelu
EmailBean Stav a funkcionalitu emailového klienta přesuneme do beanu EmailBean Vytvořte třídu EmailBean s atributy to, subject, body a user typu String vytvořte příslušné gettery a settery Vytvořte metodu send její obsah se přesune ze servletu MailServlet z metody sendMail public class EmailBean implements Serializable { private String subject; private String to; private String user; private String body; } return user; public String getUser() { this.user = user; public void setUser(String user) { return to; public String getTo() { this.to = to; public void setTo(String to) { return subject; public String getSubject() { this.subject = subject; public void setSubject(String subject) { return body; public String getBody() { this.body = message; public void setBody(String message) { /** * serializovatelnosti být tento atribut transientní. Při aktivaci * session uloženu jako hodnotu atributu, musel by kvůli * Schválně předáváme mail session jako parametr. Kdybychom měli * @throws MessagingException * @param mailSession * během migrace by tak bylo komplikované získat session zpět. // Vytvoříme objekt zprávy public void send(Session mailSession) throws MessagingException { */ Message message = new MimeMessage(mailSession); // z konfigurace serveru // Zatím nenastavujeme From, použije se default InternetAddress.parse(to, false)); message.setRecipients(Message.RecipientType.TO, //message.setFrom(); message.setSubject(subject); // Nastavíme předmět message.setText(body); // Vložíme text zprávy message.setHeader("X-Mailer", "My Mailer"); // Nastavíme hlavičku indikující mailového klienta // Nastavíme datum odeslání message.setSentDate(timeStamp); Date timeStamp = new Date(); Transport.send(message); // Odešleme zprávu
MVC – varianta Service To Worker 10: redirect emailForm.jsp (view) 1, 11: čtení dat z beanu 2: akce send (POST) EmailBean (model) MailServlet (controller) resume.jsp (view) 6: čtení dat z beanu 7: akce new (POST) 5: redirect 3, 8: aktualizace vlastností beanu 4, 9: volání business metod na beanu
Úprava MailServlet Zrušte metodu sendMail Přeřiďte metodu doGet @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Lazy inicializace email beanu EmailBean emailBean = (EmailBean) request.getSession().getAttribute("emailBean"); if (emailBean == null) { // email bean není v session, vytvoříme jej tedy createEmailBean(request, response); } else { // Pokud je email bean v session, přesměrováváme // na emailForm.jsp. response.sendRedirect("emailForm.jsp"); }
Metoda createEmailBean /** * Akce, která vytvoří nový e-mail bean a vloží jej * do session jako atribut. */ private void createEmailBean(HttpServletRequest request, HttpServletResponse response) throws IOException { EmailBean emailBean = new EmailBean(); request.getSession().setAttribute("emailBean", emailBean); // Atribut 'user' v dotazu nastavuje // autentizační filtr (FrontControllerFilter) String user = (String) request.getAttribute("user"); emailBean.setUser(user); // přesměrujeme na formulář response.sendRedirect("emailForm.jsp"); }
Metoda doPost @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Při HTTP metodě POST se očekává, že je v session // k dispozici e-mail bean. EmailBean emailBean = (EmailBean) request.getSession().getAttribute("emailBean"); if (emailBean == null) { throw new ServletException("No email session"); } try { // odešleme e-mail doSend(emailBean, request, response); } catch (MessagingException e) { throw new ServletException(e);
Metoda doSend /** * Akce, která odesílá email voláním business metody * na email beanu. */ private void doSend(EmailBean emailBean, HttpServletRequest request, HttpServletResponse response) throws IOException, MessagingException, ServletException { // Přečteme si parametry z formuláře String to = request.getParameter("to"); String subject = request.getParameter("subject"); String message = request.getParameter("message"); // Nastavíme vlastnosti e-mail beanu emailBean.setTo(to); emailBean.setSubject(subject); emailBean.setBody(message); // odešleme email emailBean.send(mailSession); // přesměrujeme na resumé response.sendRedirect("resume.jsp"); }
Změna uvítací stránky Volbu zobrazované stránky má na starosti kontroler MailServlet V MVC se na stránky obvykle neodkazuje přímo, ale nepřímo přes kontroler Uvítací stránka bude také směrována na kontroler Je třeba změnit nastavení ve WEB-INF/web.xml <welcome-file>sendMail</welcome-file> <% response.sendRedirect("sendMail"); %>
Úprava JSP stránek Upravte emailForm.jsp a resume.jsp tak, aby načítaly hodnoty z email beanu Deklarace proměnné email beanu <jsp:useBean id="emailBean" class="cz.vse.javaee.cviceni4.EmailBean" scope="session"/> Výpis vlastností beanu ${emailBean.user} nebo <jsp:getProperty name="emailBean" property="user"/> emailForm.jsp <jsp:useBean id="emailBean" class="cz.vse.javaee.cviceni4.EmailBean" scope="session"/> <h1>Formulář pro email</h1> <h2>Uživatel: ${emailBean.user}</h2> resume.jsp <h5>Nick: ${emailBean.user}</h5> Komu: ${emailBean.to}<br> Předmět: ${emailBean.subject}<br> Zpráva: ${emailBean.body}<br>
Sestavení a deploy
Akce pro nový email Zaveďte tlačítko Nový email do stránky resume.jsp Ve formuláři tlačítka uveďte skrytý parametr action s hodnotou new Do formuláře pro odesílání emailu na stránce emailForm.jsp také přidejte skrytý parametr action s hodnotou send resume.jsp <form action="sendMail" method="post"> <input type="hidden" name="action" value="new"> <input type="submit" value="Nový email"> </form> emailForm.jsp <input type="hidden" name="action" value="send">
Zpracování událostí v MailServlet @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Při HTTP metodě POST se očekává, že je v session // k dispozici e-mail bean. EmailBean emailBean = (EmailBean) request.getSession().getAttribute("emailBean"); if (emailBean == null) { throw new ServletException("No email session"); } try { // Přečteme se akci String action = request.getParameter("action"); if ("send".equals(action)) { doSend(emailBean, request, response); } else if ("new".equals(action)) { createEmailBean(request, response); } else { throw new ServletException("No action specified"); } catch (MessagingException e) { throw new ServletException(e);
Důsledky refaktoringu MailServlet se zbavil závislosti na JavaMail API EmailBean se zbavil závislosti na Servlet API velmi flexibilní snadné přidávání funkcionality – akcí
Diagram tříd v MVC Controller 1..N View Volba pohledu 1(..N) Vyvolávání akce 0..N Aktualizace modelu v důsledku zpracování akce Čtení dat pro zobrazení 1 1 Notifikace pohledům o změnách v modelu (spíše v desktopových aplikacích) Model
Posluchač atributů v HTTP session Motivace: nelíbí se nám, že MailServlet nastavuje uživatele do email beanu MailServlet se má starat o správu emailů, nikoliv o uživatele Každá nadbytečná znalost komplikuje kód komponenty Indicie – na izolovaném místě kódu třídy se pracuje s pojmem (uživatel), který se ve zbytku třídy nepoužívá Řešení – nastavení uživatele (a případně dalších souvisejících vlastností) bude řešit specializovaná komponenta Posluchač HttpSessionAttributeListener
Problematické místo v MailServlet
Třída posluchače package cz.vse.javaee.cviceni4; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; /** * Spojovací článek mezi emailovou agendou a identifikací uživatele. */ public class UserSetterForEmailBean implements HttpSessionAttributeListener { * Odchytává událost vytvoření nového email beanu. V takovém * případě nastavuje beanu aktuálního uživatele. public void attributeAdded(HttpSessionBindingEvent event) { if ("emailBean".equals(event.getName())) { // Byl založen nový email bean. EmailBean emailBean = (EmailBean) event.getValue(); adjustUser(event, emailBean); } private void adjustUser(HttpSessionBindingEvent event, EmailBean emailBean) { String user = (String)event.getSession().getAttribute("user"); emailBean.setUser(user); public void attributeReplaced(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); if ("user".equals(event.getName())) { // Při změně uživatele odstraníme starý email bean. Jeho // nová instance se vytvoří až v okamžiku potřeby (např. // <jsp:useBean> v emailForm.jsp nebo doGet v MailServlet). session.removeAttribute("emailBean"); } else // Byl založen nový email bean nahrazující starý. Novému // je třeba nastavit uživatele. EmailBean newEmailBean = (EmailBean) session.getAttribute("emailBean"); adjustUser(event, newEmailBean); @Override public void attributeRemoved(HttpSessionBindingEvent event) {
Třída posluchače - pokračování
Registrace posluchače Do WEB-INF/web.xml <listener> <description> Nastavuje uživatele do email beanu. </description> <listener-class> cz.vse.javaee.cviceni4.UserSetterForEmailBean </listener-class> </listener>
Úpravy V MailServlet odstraňte kód v metodě createEmailBean, který nastavuje uživatele FrontControllerFilter momentálně nastavuje jméno uživatele jako atribut do dotazu Posluchač má ale přístup pouze do session Ve FrontControllerFilter v metodě noAction nastavte jméno uživatele do session atributu user, místo do atributu dotazu // Nastavíme nick uživatele jako atribut session. HttpSession session = request.getSession(); String nickName = cookie.getValue(); // Nastavení pouze pokud ještě není v session. if (!nickName.equals(session.getAttribute("user"))) { session.setAttribute("user", nickName); }
Úpravy Ve FrontControllerFilter v metodě doLogout před voláním forwardToLogin zrušte session HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); }
Domácí úkol Do stránky emailForm.jsp přidejte dvě tlačítka pro akce store a restore. Akce store uloží rozepsanou zprávu do souboru nazvaného podle jména uživatele. Po vykonání akce lze pokračovat v editaci zprávy a případně ji odeslat Adresář pro ukládání souborů bude konfigurovatelný pomocí parametru ve WEB-INF/web.xml Akce restore vyplní políčka formuláře podle údajů uložených posledním voláním akce store. Logika akcí bude v metodách třídy EmailBean