A camada de modelo dados de nosso projeto já está pronta. Nesta segunda parte vamos criar camada view, que é a camada responsável por gerar a interface de comunicação entre o usuário e a nossa aplicação. Para tanto utilizaremos os frameworks Java Server Pages (JSP) e Servlets.
O Diagrama de Casos de Uso abaixo nos mostra todas as possíveis ações que o usuário pode realizar no nosso sistema:
As ações em um diagrama de casos de uso podem ser mapeadas para um método na classse responsável, geralmente chamada service. Por exemplo, quando usuário selecionar uma categoria, o sistema deve mostrar-lhe algum tipo de lista contendo todos os produtos da categoria selecionada. Para satisfazer esse caso de uso poderíamos criar um método cuja assinatura é parecida com esta List<Product> getProducts(Categorie cat).
JSP: criando a interface com o usuário
Todas as páginas de nossa aplicação terão o design semelhante, com um cabeçalho, exibindo o nome da loja; um painel de menus do lado esquerdo, mostrando as categorias; e no centro o conteúdo variável, listas de produtos, descrições, carinho de compras, etc. Como mostrado na figura:
Com o NetBenas aberto crie a pasta pages dentro de Web Pages, que é onde ficarão todas as páginas web da nossa aplicação. Em seguida clique com o botão direito do mouse na pasta pages, new, JSP... Crie as páginas:
- header.jsp (o cabeçalho que será usado por todas as páginas)
- menu.jsp (o menu lateral que será usado por todas as páginas)
- home.jsp (página inicial)
- productsByCat.jsp (exibe os produtos por filtrados por categoria)
- productDetails.jsp (exibe os detalhes de um produto selecionado)
- shoppingCart.jsp (exibe os produtos no carrinho de compras)
- checkout.jsp (exibe os dados do pedido para o usuário confirmar a compra)
- login.jsp (para fazer pedido usuário precisa fazer login)
- register.jsp (para fazer login o usuário precisa informar os dados e cadastrar)
- thankyou.jsp (mensagem de agradecimento após a compra)
Crie também um pacote chamado control. Dentro dele crie uma classe chamada ServletController. Está classe será responsável por gerenciar a navegação entre as páginas da aplicação:
Controlando navegação de páginas
Nossa aplicação segue o padrão Model-View-Controller (MVC). Uma servlet intercepta toda requisição HTTP e encaminha a resposta para ser apresentada pela página JSP correspondente baseado na URL da requisição, nos input parameters e no estado da aplicação. O valor do parâmetro action em cada página JSP define o comportamento da servlet.
ServletController
Declaração da classe e o método init():
//imports omitidos //@WebServlet diz para o servidor que esta classe é uma servlet, e qual é a sua url no browser //@WebInitParam indica parâmetros de inicialização @WebServlet(name = "ServletController", urlPatterns = {"/servletController"}, initParams = { @WebInitParam(name = "controller", value = "servletController"), @WebInitParam(name = "imagesUrl", value = "images/")}) public class ServletController extends HttpServlet{ @Override public void init(ServletConfig config) throws ServletException { //configura os parâmetros de inicialização em ServletContext, //de modo que eles ficarão acessíveis de qualquer parte da aplicação, em qualquer momento ServletContext context = config.getServletContext(); context.setAttribute("controller", config.getInitParameter("controller")); context.setAttribute("imagesUrl", config.getInitParameter("imagesUrl")); } }
O método doGet() geralmente é usado para exibir informações de consulta com base em parâmetros. Não é um método adequado para para enviar enviar informações sensíveis pois o valor dos parâmetros aparece na URL:
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //recebe o parâmetro action e com base no valor decide para qual página despachar String action = req.getParameter("action"); String url = "/pages/home.jsp"; if(action != null){ //já existe uma sessão aberta ClientService clientService = (ClientService) req.getSession().getAttribute("clientService"); if(action.equals("productsByCat")){ url = "/pages/productsByCat.jsp"; String categoryId = req.getParameter("categoryId"); req.setAttribute("categoryId", categoryId); } else if(action.equals("productDetails")){ url = "/pages/productDetails.jsp"; String productId = req.getParameter("productId"); req.setAttribute("productId", productId); } else if(action.equals("addProductToCart")){ clientService.addProductToCart(req.getParameter("productId")); url = "/pages/shoppingCart.jsp"; } else if(action.equals("displayShoppingCart")){ url = "/pages/shoppingCart.jsp"; } //o usuário quer efetivar o pedido, mas antes precisa fazer login, //se não tiver login, terá que se cadastrar else if(action.equals("checkout")){ boolean logged = clientService.isLogged(); if(logged) url = "/pages/checkout.jsp"; else url = "/pages/login.jsp"; } else if(action.equals("register")){ url = "/pages/register.jsp"; } } //direciona para a página adequada req.getRequestDispatcher(url).forward(req, resp); }
O método doPost() é usado para enviar informações de formulário. Na nossa aplicação ele utilizado para login, cadastro do usuário, finalizar o pedido e mudar o estado do carrinho de compras.
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //recupera action e decide o que fazer com base no valor String action = req.getParameter("action"); String url = "/pages/shoppingCart.jsp"; if(action != null){ ClientService clientService = (ClientService) req.getSession().getAttribute("clientService"); if(action.equals("updateCart")){ String quantity = req.getParameter("quantity"); String productId = req.getParameter("productId"); clientService.updateShoppingCart(productId, quantity); } else if(action.equals("deleteProduct")){ String productId = req.getParameter("productId"); clientService.deleteProduct(productId); } else if(action.equals("register")){ clientService.register(req); url = "/pages/checkout.jsp"; } else if(action.equals("checkout")){ EOrder order = clientService.proceedCheckout(); req.setAttribute("order", order); url = "/pages/thankyou.jsp"; } else if(action.equals("login")){ clientService.login(req.getParameter("email"), req.getParameter("login")); if(clientService.isLogged()) url = "/pages/checkout.jsp"; else url = "/pages/login.jsp"; } } req.getRequestDispatcher(url).forward(req, resp); }Páginas JSP
As páginas header.jsp e menu.jsp estarão presentes em todas as páginas, de modo que elas serão páginas de composição através da declaração tag jsp:include nas outras páginas, assim não é necessário escrever o mesmo código repetidas vezes.
header.jsp
<center> <table> <tr> <td><img src="${applicationScope['imagesUrl']}stark.png" style="width: 80px; height: 70px;" /></td> <td> <a href="${applicationScope['controller']}"> <h4>STARK HOUSE E-commerce - BuyDigital!</h4> </a> </td> <td> <a href="${applicationScope['controller']}?action=displayShoppingCart"> <img src="${applicationScope['imagesUrl']}shopping_cart.png" style="width: 50px; height: 50px;" /> </a> </td> </tr> </table> </center>menu.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <jsp:useBean id="categoryService" class="business.CategoryService" scope="session"/> <jsp:useBean id="clientService" class="business.ClientService" scope="session"/> <form> <table> <c:if test="${clientService.logged}"> <tr> Bem Vindo ${clientService.client.fullName} </tr> </c:if> <tr> <td> <input type="text" name="search"/> </td> <td><input type="button" value="SEARCH"/></td> </tr> <c:forEach items="${categoryService.categories}" var="category"> <tr> <td> <!-- parâmetro action e categoryId são enviados à servlet controller quando o usuário usar este link --> <a href="${applicationScope['controller']}?action=productsByCat&categoryId=${category.id}"> ${category.name}</a> </td> </tr> </c:forEach> </table> </form>Nas páginas JSP a expressão ${applicationScope['attributeName']} representa um atributo de contexto da apĺicação, aqueles mesmos que foram definidos no método initi() da Servlet. Como o próprio nome sugere, tais atributos têm escopo de aplicação, ficam disponíveis enquanto a aplicação rodar no servidor.
O valor do parâmetro action vai variando de acordo com o link do qual ele é enviado para o controller. Na página menu.jsp seu valor é productsByCat, indicando ao controller para carregar uma exibição de produtos filtrados por categoria. O id da categoria é informado em um segundo parâmetro chamado categoryId.
A tag jsp:useBean permite que você crie ou localize uma instância de uma classe. Como o escopo informado é sessão, o bean será criado apenas uma vez, e ficará diponível enquanto durar a sessão do usuário.
Home.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <img src="${applicationScope['imagesUrl']}store.jpg" style="width: 150px; height: 150px;" /> </td> </tr> </table> </body> </html>productsByCat.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="productService" class="business.ProductService" scope="session"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <table style="width: 500px"> <tr> <th>Name</th> <th>Price</th> </tr> <c:forEach items="${productService.getProducts(requestScope.categoryId)}" var="product"> <tr> <td>${product.name}</td> <td>${product.price}</td> <td> <a href="${applicationScope['controller']}?action=productDetails&productId=${product.id}"> Details...</a> </td> </tr> </c:forEach> </table> </td> </tr> </table> </body> </html>A página productsByCat lista os produtos de uma categoria. A tag <c:ForEach/> realiza um loop pela coleção retornada pela chamada do método getProducts() do bean productService.
productsDetails.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="productService" class="business.ProductService" scope="session"/> <c:set var="product" value="${productService.getProduct(requestScope.productId)}" scope="request" /> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <table> <th colspan="2"> ${product.name} </th> <tr> <td colspan="2">${product.description}</td> </tr> <tr style="float: left;"> <td>Price $</td> <td>${product.price}</td> </tr> <tr> <td> <a href="${applicationScope['controller']}?action=addProductToCart&productId=${product.id}"> Add To Cart</a> </td> </tr> </table> </td> </tr> </table> </body> </html>A tag <c:set/> permite que você assine um valor para um objeto Y ou uma variável qualquer. Você deve referenciar essa variável no restante da página por meio da Expression Language (EL) ${Y}.
shoppingCart.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="clientService" class="business.ClientService" scope="session"/> <jsp:useBean id="productService" class="business.ProductService" scope="session"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <c:if test="${clientService.shoppingCart.size() != null}" > <table> <tr> <th>Name</th> <th>Description</th> <th>Price</th> <th>Quantity</th> <th>Subtotal</th> </tr> <c:forEach items="${clientService.shoppingCart.keySet()}" var="idProduct" > <c:set var="product" value="${productService.getProduct(idProduct)}" /> <tr> <td>${product.name}</td> <td>${product.description}</td> <td>${product.price}</td> <form method="post" action="${applicationScope['controller']}"> <td> <input type="hidden" name="action" value="updateCart" /> <input type="text" name="quantity" size="2" value="${clientService.shoppingCart.get(product.id)}" /> </td> <td>${(product.price * clientService.shoppingCart.get(product.id))}</td> <td> <input type="submit" value="Update" /> <input type="hidden" name="productId" value="${product.id}" /> <input type="hidden" name="action" value="updateShoppingCart" /> </td> </form> <form method="post" action="${applicationScope['controller']}"> <td> <input type="submit" value="Delete" /> <input type="hidden" name="productId" value="${product.id}" /> <input type="hidden" name="action" value="deleteProduct" /> </td> </form> </tr> </c:forEach> </table> <a href="${applicationScope['controller']}?action=checkout" >Proceed Checkout</a> </c:if> <c:if test="${clientService.shoppingCart == null}"> Shopping Cart is Empty! </c:if> </td> </tr> </table> </body> </html>shoppingCart.jsp representa o carrinho de compras. A medida em que o usuário escolhe os produtos, eles são armazenados na sessão na forma de um map chave/valor representando produto/quantidade respectivamente. Não é uma boa prática utilizar código java puro em paginas JSP, dê prefêrencia para as tagLibs JSTL, como temos feitos até agora com as tags <c:if>, <c:set>, <c:forEach> etc.
checkout.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="clientService" class="business.ClientService" scope="session"/> <c:set var="client" value="${clientService.getClient()}" scope="request" /> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Register</title> </head> <body> <table align="center"> <tr> <td> <jsp:include page="header.jsp" flush="true" /> </td> </tr> <tr> <td> <form method="post" action="${applicationScope['controller']}"> <input type="hidden" name="action" value="checkout"/> <table style="border: 1px solid #cccccc; width: 450px;"> <tr> <td>Name:</td> <td colspan="3">${client.fullName}</td> </tr> <tr> <td>Email</td> <td colspan="3">${client.email}</td> </tr> <tr> <td align="center" colspan="4"><b>Your Order Details</b></td> </tr> <tr style="font-weight: bold;"> <td>Product</td> <td>Price</td> <td>Quantity</td> <td>Total Item</td> </tr> <c:forEach items="${clientService.shoppingCart.keySet()}" var="idProduct" > <c:set var="product" value="${productService.getProduct(idProduct)}" /> <tr> <td>${product.name}</td> <td>${product.price}</td> <td>${clientService.shoppingCart.get(product.id)}</td> <td>${(product.price * clientService.shoppingCart.get(product.id))}</td> </tr> </c:forEach> <tr> <td colspan="2">Total Order</td> <td colspan="2">${clientService.totalOrder}</td> </tr> <tr> <td colspan="2">Credit Card Number:</td> <td colspan="2">${client.creditCard}</td> </tr> <tr> <td colspan="4"><input type="submit" value="Confirm Order"/></td> </tr> </table> </form> </td> </tr> </table> </body> </html>checkout.jsp exibe um formulário com todos os item que o usário escolheu e o valor total do pedido.
login.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td>= </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <table> <form method="post" action="${applicationScope['controller']}"> <input type="hidden" name="action" value="login"/> <tr> <th colspan="2">Log in to preceed your Checkout</th> </tr> <tr> <td>Email</td> <td><input type="text" name="email" /></td> </tr> <tr> <td>Login</td> <td><input type="text" name="login" /></td> </tr> <tr> <td><input type="submit" value="Login"/></td> </tr> <tr> <th colspan="2"> <a href="${applicationScope['controller']}?action=register">Not Registered Yet?</a> </th> </tr> </form> </table> </td> </tr> </table> </body> </html>login.jsp pede para que o usuário faça login antes de emitir o pedido, caso ele não seja cadastrado, terá que fazê-lo no formulário de registro.
register.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@page contentType="text/html" pageEncoding="UTF-8"%> <jsp:useBean id="clientService" class="business.ClientService" scope="session"/> <jsp:useBean id="productService" class="business.ProductService" scope="session"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> <form action="${applicationScope['controller']}" method="post"> <input type="hidden" name="action" value="register"/> <table> <tr> <th colspan="4">Informations Required to Proceed Checkout</th> </tr> <tr> <td>First Name</td> <td colspan="3"><input type="text" name="fName" required="true" /></td> </tr> <tr> <td>Last Name</td> <td colspan="3"><input type="text" name="lName" required="true" /></td> </tr> <tr> <td>Birth Date</td> <td colspan="3"><input type="text" name="birthDate" required="true" />dd/mm/yyyy</td> </tr> <tr> <td>Email</td> <td><input type="text" name="email" required="true" /></td> <td>Login</td> <td><input type="text" name="login" required="true"/></td> </tr> <tr> <td>Credit Card</td> <td colspan="3"><input type="text" name="creditCard" required="true" /></td> </tr> <tr> <td colspan="4"> <input type="submit" value="Register" /> </td> </tr> </table> </form> </td> </tr> </table> </body> </html>thankyou.jsp exibe uma mensagem de agradecimento após o usuário terminar o pedido:
<%@page contentType="text/html" pageEncoding="UTF-8"%> <c:set var="order" value="${requestScope.order}" scope="request" /> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <table style="border: 1px solid #cccccc; width: 1000px"> <tr> <td colspan="2"><jsp:include page="header.jsp" flush="true" /></td> </tr> <tr> <td><jsp:include page="menu.jsp" flush="true" /></td> <td> Thank You for purchasing, ${order.client.firstName}!<br/> Your order ID is ${order.id}, you will receive a confirmation at ${order.client.email}! </td> </tr> </table> </body> </html>Uma outra estrátégia para enviar o parâmetro action para a ServletController é utilizar campos escondidos com a tag input como por exemplo esta definição na página checkout.jsp
<input type="hidden" name="action" value="checkout"/>.
Neste ponto, o projeto não compila porque ela faz referência aos beans de serviço que ainda não foram criados, como a classe ClientService por exemplo. Essas classes serão implementadas na parte 3 do artigo.
Referências
KURNIAWAN, Budi. Java for the Web with Servlets, JSP, and EJB: A Developer's Guide to Scalable J2EE Solutions. 1. ed. Indianapolis: New Riders, 2002. 903 p.
PATZER, Andrew. Foundations of JSP Design Patterns. 1. ed. Berkeley: Apress, 2004. 282 p.
Não tem a parte 03?
ReplyDeleteai quebra as pernas, cade a parte 3 :(
ReplyDeleteCool and I have a keen offer you: How To Become A House Renovation remodeling and renovation
ReplyDelete