blob: 84bd592707b1e43317c4d7be0573d0906fde483b [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= Tutorial do NetBeans E-commerce: Gerenciando Sessões
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Tutorial do NetBeans E-commerce: Gerenciando Sessões - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Tutorial do NetBeans E-commerce: Gerenciando Sessões
1. link:intro.html[+Introdução+]
2. link:design.html[+Criando a Aplicação+]
3. link:setup-dev-environ.html[+Configurando o Ambiente de Desenvolvimento+]
4. link:data-model.html[+Criando o Modelo de Dados+]
5. link:page-views-controller.html[+Preparando as Views de Página e o Servlet do Controlador+]
6. link:connect-db.html[+Conectando a Aplicação ao Banco de Dados+]
7. link:entity-session.html[+Adicionando Classes de Entidades e Beans de Sessão+]
8. *Gerenciando Seções*
9. link:transaction.html[+Integrando a Lógica de Negócios Transacional+]
10. link:language.html[+Adicionando o Suporte a Idioma+]
11. link:security.html[+Protegendo a Aplicação+]
12. link:test-profile.html[+Testando e Criando Perfis+]
13. link:conclusion.html[+Conclusão+]
image::../../../../images_www/articles/68/netbeans-stamp-68-69.png[title="O conteúdo desta página se aplica ao NetBeans IDE, versões e 6.8 e 6.9"]
Cada aplicação de e-commerce que oferece alguma forma de funcionalidade de carrinho de compras precisa poder lembrar dados específicos do usuário, conforme os usuários clicam no site. Infelizmente, para o desenvolvedor, o protocolo HTTP, sobre o qual a comunicação na Internet ocorre, é um protocolo _sem estado_. Cada solicitação recebida pelo servidor é uma informação independente que não tem nenhuma relação com solicitações recebidas anteriormente. Portanto, se o cliente clicar em um botão para adicionar um item ao seu carrinho de compras, a aplicação deverá tomar medidas para assegurar não somente que o estado do carrinho do usuário seja atualizado, mas que a ação não afete o carrinho de outro usuário que pode estar navegando pelo site ao mesmo tempo.
A fim de tratar corretamente o cenário descrito acima, é necessário implementar uma funcionalidade, de maneira que uma _sessão_ possa ser criada e mantida durante toda a visita do usuário ao site. A tecnologia Servlet, que é a base de todas as aplicações web baseadas em Java, fornece isso com sua interface link:http://java.sun.com/javaee/6/docs/api/javax/servlet/http/HttpSession.html[+`HttpSession`+]. É necessário também definir várias classes, como, por exemplo, `ShoppingCart` e `ShoppingCartItem`, que permitem à aplicação armazenar temporariamente dados do usuário, enquanto a sessão é mantida.
Esta unidade do tutorial utiliza uma abordagem diferente das outras no Tutorial do NetBeans E-commerce. Ao invés de criar os arquivos de projeto e fornecer as etapas com snippets de código para copiar e colar no seu próprio projeto, você abre o snapshot do projeto completo para esta unidade e examina o código utilizando o depurador do IDE e outras ferramentas. No processo, será possível aprender como aplicar um objeto `HttpSession` ao seu código, de forma que cada visita ao website resulte em uma sessão dedicada. Você aprenderá também sobre _variáveis de escopo_ e seu uso nas classes Java e nas páginas JSP. Esta unidade também discute o mecanismo default de `HttpSession` para manutenção de sessões (ex., cookies) e mostra que medidas devem ser tomadas no caso de os cookies estarem desativados no browser do usuário. Por fim, os time-outs de sessão são cobertos e a unidade demonstra como tratá-los criando um filtro simples que intercepta solicitações de verificação da existência da sessão.
Você pode exibir uma demonstração ao vivo da aplicação construída neste tutorial: link:http://dot.netbeans.org:8080/AffableBean/[+Aplicação de Demonstração do Tutorial do NetBeans E-commerce+]
|===
|Software ou Recurso |Versão Necessária
|link:https://netbeans.org/downloads/index.html[+NetBeans IDE+] |Pacote Java, 6.8 ou 6.9
|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java Development Kit (JDK)+] |versão 6
|<<glassFish,GlassFish Server>> |v3 ou Open Source Edition 3.0.1
|link:http://dev.mysql.com/downloads/mysql/[+Servidor de banco de dados MySQL+] |versão 5.1
|link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252Fecommerce%252FAffableBean_snapshot5.zip[+Projeto AffableBean+] |snapshot 5
|===
*Observações:*
* O NetBeans IDE requer o Java Development Kit (JDK) para ser executado adequadamente. Se você não tiver nenhum dos recursos listados acima, o JDK deverá ser o primeiro item a ser submetido a download e instalado.
* O Pacote Java do NetBeans IDE inclui o Java Web e tecnologias EE, que são necessárias para a aplicação construída neste tutorial.
* O pacote Java do NetBeans IDE também inclui o GlassFish Server, necessário para este tutorial. Você poderia link:https://glassfish.dev.java.net/public/downloadsindex.html[+fazer download do GlassFish Server independentemente+], mas a versão fornecida pelo NetBeans do qual fez download tem a vantagem adicional de ser registrada automaticamente no IDE.
* Você pode seguir esta unidade do tutorial sem ter concluído as unidades anteriores. Para isso, consulte as link:setup.html[+instruções de configuração+], que descrevem como preparar o banco de dados e estabelecer uma conectividade entre o IDE, o GlassFish e o MySQL.
[[session-data]]
== Tratando Dados de Sessão
As aplicações podem gerenciar sessões de usuário com o objeto `HttpSession`. Você pode vincular dados específicos do usuário ao objeto `HttpSession` e, em seguida, acessar esses dados em um estágio mais avançado. As duas ações de vincular e acessar podem ser feitas por meio das classes Java, assim como usando variáveis de escopo de sessão em expressões EL.
* <<httpSession,Trabalhando com um Objeto HttpSession>>
* <<scopedVariables,Trabalhando com Variáveis de Escopos em Aplicações Web>>
[[httpSession]]
=== Trabalhando com um Objeto HttpSession
A aplicação `AffableBean` utiliza o objeto `HttpSession` para identificar usuários em várias solicitações. Um objeto `HttpSession` é obtido utilizando `getSession()` em uma determinada solicitação:
[source,java]
----
HttpSession session = request.getSession();
----
Se ainda não existir um objeto de sessão para a solicitação, o método cria e retorna um novo objeto de sessão.
Você pode utilizar o objeto de sessão como um veículo para a transferência de dados entre as solicitações. Você pode utilizar o método `setAttribute` para vincular objetos à sessão. Da mesma forma, você usa `getAttribute` para recuperar objetos da sessão. Na aplicação `AffableBean`, por exemplo, o carrinho de compras do usuário é criado e vinculado à sessão do usuário da seguinte forma:
[source,java]
----
ShoppingCart cart = new ShoppingCart();
session.setAttribute("cart", cart);
----
Para recuperar o carrinho da sessão, o método `getAttibute` é aplicado:
[source,java]
----
cart = (ShoppingCart) session.getAttribute("cart");
----
Nas páginas JSP, você pode acessar objetos vinculados à sessão utilizando expressões EL. Continuando com o exemplo acima, se um objeto `ShoppingCart` chamado "`cart`" estiver vinculado à sessão, você poderá acessá-lo utilizando a seguinte expressão EL:
[source,java]
----
${cart}
----
No entanto, acessar o objeto `ShoppingCart` em si tem pouco valor. O que você realmente quer é uma maneira de acessar valores armazenados no objeto. Se explorar a nova classe `ShoppingCart` no snapshot do projeto, você notará que ela contém as seguintes propriedades:
* `double total`
* `int Numberofitems `
* `List<String, ShoppingCartItem> items`
Desde que as propriedades tenham métodos getter correspondentes, você poderá acessar valores para propriedades singulares utilizando notação de ponto simples em uma expressão EL. Se examinar a página `cart.jsp`, verá que é exatamente assim que o valor para `numberOfItems` é acessado:
[source,html]
----
<p>Your shopping cart contains ${cart.numberOfItems} items.</p>
----
Para extrair dados de propriedades que contêm valores múltiplos, tal como a lista `items` acima, a página `cart.jsp` utiliza um loop `<c:forEach>`:
[source,xml]
----
<c:forEach var="cartItem" items="${cart.items}" varStatus="iter">
<c:set var="product" value="${cartItem.product}"/>
<tr class="${((iter.index % 2) == 0) ? 'lightBlue' : 'white'}">
<td>
<img src="${initParam.productImagePath}${product.name}.png"
alt="${product.name}">
</td>
<td>${product.name}</td>
<td>
&amp;euro; ${cartItem.total}
<br>
<span class="smallText">( &amp;euro; ${product.price} / unit )</span>
</td>
...
</tr>
</c:forEach>
----
A propriedade `product` de `ShoppingCartItem` identifica o tipo de produto de um item do carrinho. O loop acima aproveita isso definindo primeiramente uma variável `product` para a expressão `${cartItem.product}`. Em seguida ele utiliza a variável para obter informações sobre esse produto (ex., nome e preço).
[[scopedVariables]]
=== Trabalhando com Variáveis de Escopos em Aplicações Web
Quando se trabalha com a tecnologia JSP/Servlet, há quatro objetos de escopo disponíveis no realm da aplicação. A tecnologia JSP implementa _objetos implícitos_ que permitem acessar classes definidas pela API do Servlet.
|===
|Escopo |Definição |Classe Servlet |Objeto Implícito JSP
|*Aplicação* |Memória global para uma aplicação web |`link:http://java.sun.com/javaee/6/docs/api/javax/servlet/ServletContext.html[+javax.servlet.ServletContext+]` |`applicationScope`
|*Sessão* |Dados específicos para uma sessão de usuário |`link:http://java.sun.com/javaee/6/docs/api/javax/servlet/http/HttpSession.html[+javax.servlet.http.HttpSession+]` |`sessionScope`
|*Solicitação* |Dados específicos para uma solicitação de servidor individual |`link:http://java.sun.com/javaee/6/docs/api/javax/servlet/http/HttpServletRequest.html[+javax.servlet.HttpServletRequest+]` |`requestScope`
|*Página* |Dados que são válidos somente no contexto de uma única página (somente JSPs) |`[n/d]` |`pageScope`
|===
Se abrir o arquivo `category.jsp` do projeto no editor, verá que as expressões EL incluem diversas variáveis de escopo, incluindo `${categories}`, `${selectedCategory}` e `${categoryProducts}`. A variável `${categories}` é uma aplicação de escopo, definida no método `init` do `ControllerServlet`:
[source,java]
----
// store category list in servlet context
getServletContext().setAttribute("categories", categoryFacade.findAll());
----
As outras duas, `${selectedCategory}` e `${categoryProducts}`, são inseridas no escopo da sessão da aplicação a partir do `ControllerServlet`. Por exemplo:
[source,java]
----
// place selected category in session scope
session.setAttribute("selectedCategory", selectedCategory);
----
*Observação:* Se você estiver continuando de unidades anteriores do tutorial, provavelmente notará que `${selectedCategory}` e `${categoryProducts}` foram originalmente colocadas no escopo da solicitação. Em unidades anteriores, isso não era problema, mas considere agora o que acontece se um usuário clicar no botão "adicionar ao carrinho" na página de uma categoria. O servidor responde a uma solicitação `addToCart` retornando a página de categoria em exibição no momento. Portanto, é preciso saber a `selectedCategory` e a `categoryProducts` pertencentes à categoria selecionada. Em vez de estabelecer essas informações para cada solicitação, você as coloca no escopo da sessão de uma solicitação de `category` para que sejam mantidas em várias solicitações e possam ser acessadas quando necessário. Além disso, examine a funcionalidade fornecida pela página do carrinho. (Uma descrição funcional é <<cartPage,fornecida abaixo>>.) O botão "continuar comprando" retorna o usuário à categoria exibida anteriormente. Novamente, as variáveis `selectedCategory` e `categoryProducts` são necessárias.
Ao fazer referência às variáveis do escopo em uma expressão EL, você não precisa especificar o escopo da variável (desde que você não tenha duas variáveis com o mesmo nome em escopos diferentes). O mecanismo JSP verifica todos os quatro escopos e retorna a primeira variável correspondente que encontrar. Em `category.jsp` por exemplo, a expressão:
[source,java]
----
${categoryProducts}
----
é uma abreviação para:
[source,java]
----
${sessionScope.categoryProducts}
----
[tips]#Para obter mais informações, consulte os recursos a seguir:#
* link:http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/web-tier/web-tier5.html#1079198[+Criando Aplicações Corporativas com a Plataforma J2EE: Escopos de Estado+]
* link:http://download.oracle.com/docs/cd/E17477_01/javaee/5/tutorial/doc/bnafo.html[+Compartilhando Informações > Usando Objetos de Escopo+]
* link:http://download.oracle.com/docs/cd/E17477_01/javaee/5/tutorial/doc/bnahq.html#bnaij[+Linguagem de Expressão Unificada > Objetos Implícitos+]
[[debug]]
== Examinando Dados de Sessão com o Depurador Java
Comece explorando como a aplicação se comporta durante o runtime. Utilize o depurador do IDE para avançar pelo código e examinar como a `HttpSession` é criada e como outros objetos podem ser colocados no escopo da sessão para serem recuperados em um ponto mais adiante.
1. Abra o link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252Fecommerce%252FAffableBean_snapshot5.zip[+snapshot do projeto+] para esta unidade do tutorial no IDE. Clique no botão Abrir Projeto (image::images/open-project-btn.png[]) e use o assistente para navegar para o local em seu computador em que você fez download do projeto. Se estiver prosseguindo de uma link:entity-session.html[+unidade de tutorial anterior+], observe que este snapshot do projeto inclui um novo pacote `cart`, que contém as classes `ShoppingCart` e `ShoppingCartItem`. E note também que os seguintes arquivos foram modificados:
* `WEB-INF/web.xml`
* `css/affablebean.css`
* `WEB-INF/jspf/header.jspf`
* `WEB-INF/jspf/footer.jspf`
* `WEB-INF/view/cart.jsp`
* `WEB-INF/view/category.jsp`
* `WEB-INF/view/checkout.jsp`
* `controller/ControllerServlet`
[start=2]
. Execute o projeto (image::images/run-project-btn.png[]) para garantir que esteja configurado corretamente com seu banco de dados e servidor de aplicações.
Se receber uma mensagem de erro ao executar o projeto, consulte novamente as link:setup.html[+instruções de configuração+], que descrevem como preparar um banco de dados e estabelecer conectividade entre o IDE, o GlassFish e o MySQL.
[start=3]
. Teste a funcionalidade da aplicação no browser. Se estiver prosseguindo diretamente da link:entity-session.html[+unidade anterior do tutorial+], notará as seguintes melhorias.
=== página de categoria
* Clicar em "adicionar ao carrinho" ativa, pela primeira vez, o carrinho de compras e os widgets "avançar para check-out" para serem exibidos no cabeçalho.
* Clicar em "adicionar ao carrinho" resulta em uma atualização no número de itens do carrinho no widget do carrinho de compras do cabeçalho.
* Clicar em "exibir carrinho" resulta na exibição da página do carrinho.
* Clicar em "avançar para check-out" resulta na exibição da página de check-out.
image::images/category-page.png[title="A página Categoria inclui a funcionalidade do carrinho de compras"]
[[cartPage]]
=== página do carrinho
* Clicar em "limpar carrinho" resulta no esvaziamento dos itens do carrinho de compras.
* Clicar em "continuar comprando" resulta em um retorno à categoria exibida previamente.
* Clicar em "avançar para check-out" resulta na exibição da página de check-out.
* Inserir um número (1 a 99) no campo de quantidade de itens e, em seguida, clicar em "atualizar" resulta no recálculo do preço total para o item e do subtotal.
* Inserir zero no campo de quantidade de um item e, em seguida, clicar em "atualizar" resulta na remoção do item da tabela exibida.
image::images/cart-page.png[title="A página Carrinho inclui a funcionalidade do carrinho de compras"]
=== página de check-out
* Clicar em "exibir carrinho" resulta na exibição da página do carrinho.
* Clicar em "submeter compra" resulta na exibição da página de confirmação (sem dados específicos do usuário).
image::images/checkout-page.png[title="A página Check-out inclui a funcionalidade do carrinho de compras"]
[start=4]
. Utilize a caixa de diálogo Ir para Arquivo para abrir o `ControllerServlet` no editor. Pressione Alt-Shift-O (Ctrl-Shift-O no Mac), digite "`Controller`" na caixa de diálogo e clique em OK.
image::images/go-to-file-dialog.png[title="Use a caixa de diálogo Ir para Arquivo para abrir rapidamente recursos do projeto no editor"]
[start=5]
. Defina um ponto de interrupção no método `doPost` na linha que cria um objeto `HttpSession` (linha 150). Para definir um ponto de interrupção, clique na margem esquerda do editor.
image::images/breakpoint.png[title="Clique na margem esquerda do editor para definir pontos de interrupção"]
Para alternar números de linhas para o editor, clique com o botão direito do mouse na margem esquerda e selecione Exibir Número de Linhas.
[start=6]
. Execute o depurador. Clique no botão Depurar Projeto (image::images/debug-project-btn.png[]) na barra de ferramentas principal do IDE. O GlassFish Server será iniciado (ou reiniciado, se já estiver sendo executado) e abrirá um soquete no número da porta de depuração. A página de boas-vindas da aplicação será aberta no browser.
É possível exibir e modificar o número da porta do depurador a partir da janela Servidores (Ferramentas > Servidores). Selecione a guia Java para o servidor que está utilizando. Especifique o número da porta no campo "Endereço a ser utilizado" em Definições de Depuração.
[start=7]
. Quando a página de boas-vindas da aplicação for exibida no browser, clique em qualquer imagem da categoria para navegar para a página da categoria. Lembre-se de que quando você clicar no botão "adicionar ao carrinho" uma solicitação `addToCart` será enviada ao servidor:
[source,java]
----
<form action="addToCart" method="post">
----
Como você deve se lembrar de link:page-views-controller.html#controller[+Preparando as Views de Página e o Servlet do Controlador+], o método `doPost` de `ControllerServlet` trata solicitações para o padrão de URL `/addToCart`. Você poderá, portanto, esperar que quando um usuário clica no botão "adicionar ao carrinho", o método `doPost` é chamado.
[start=8]
. Clique em "adicionar ao carrinho" para qualquer produto na página da categoria. Volte para o IDE e note que o depurador é suspenso no ponto de interrupção.
image::images/breakpoint-suspended.png[title="O depurador é suspenso em pontos de interrupção no editor"]
[start=9]
. Posicione o cursor na chamada para `getSession()` e pressione Ctrl-Espaço para chamar a documentação do Javadoc.
image::images/javadoc-getsession.png[title="Pressione Ctrl-Espaço para chamar a documentação do Javadoc"]
De acordo com a documentação, a `getSession()` retorna a `HttpSession` atualmente associada à solicitação e, caso não exista nenhuma sessão, o método cria um novo objeto de sessão.
=== Aproveitando o Suporte do Javadoc do IDE
O IDE fornece suporte incorporado para Javadoc para o desenvolvimento do Java EE. O IDE agrega a especificação da API do Java EE 6, que pode ser aberta em um browser externo selecionando Ajuda > Referências do Javadoc > Java EE 6.
O IDE inclui também várias outras funcionalidades que possibilitam um fácil acesso à documentação da API:
* *Janela Javadoc:* Selecione Janela > Outros > Javadoc. A janela Javadoc será aberta na região inferior do IDE e exibirá a documentação da API relevante à localização do seu cursor no editor.
* *Pesquisa Javadoc por Índice:* Selecione Ajuda > Pesquisa Javadoc por Índice (Shift-F1; fn-Shift-F1 no Mac). Digite o nome da classe que está procurando e, em seguida, selecione uma classe nos resultados listados. A descrição completa da classe a partir da Especificação da API será exibida no painel inferior da janela.
* *Popup da documentação no editor:* a documentação Javadoc é exibida em uma janela de popup ao pressionar Ctrl-Espaço ou em um determinado elemento no editor. Você pode clicar no botão Navegador Externo (image::images/external-browser-btn.png[]) para abrir a documentação no seu browser. Se desejar utilizar as teclas Ctrl-Espaço somente para a funcionalidade autocompletar código, poderá desativar o popup da documentação abrindo a janela Opções (Ferramentas > Opções; NetBeans > Preferências no Mac) e, em seguida, selecionando Editor > Autocompletar Código. Desmarque a opção "Janela de Popup Automático da Documentação".
Ao documentar seu próprio trabalho, considere a adição de comentários Javadoc às suas classes e métodos. Abra a classe `ShoppingCart` e examine os comentários Javadoc adicionados aos métodos da classe. Os comentários Javadoc são marcados pelos delimitadores `/** ... */`. Por exemplo, o método `addItem` tem o seguinte comentário antes da sua assinatura:
[source,xml]
----
/**
* Adds a <code>ShoppingCartItem</code> to the <code>ShoppingCart</code>'s
* <code>items</code> list. If item of the specified <code>product</code>
* already exists in shopping cart list, the quantity of that item is
* incremented.
*
* @param product the <code>Product</code> that defines the type of shopping cart item
* @see ShoppingCartItem
*/
public synchronized void addItem(Product product) {
----
Isso lhe permite (assim como aos outros que estejam trabalhando no projeto) exibir a documentação Javadoc no método. Para demonstrar, abra o Navegador (Ctrl-7; ⌘-7 no Mac) e passe o mouse sobre o método `addItem`.
image::images/javadoc-additem.png[title="Passe o mouse sobre métodos no Navegador para exibir documentação do Javadoc"]
Você também pode utilizar o IDE para gerar um conjunto de páginas HTML do Javadoc. Na janela Projetos, clique com o botão direito do mouse no nó do projeto e selecione Gerar Javadoc. O IDE gerará o Javadoc na pasta `dist/javadoc` no diretório do projeto e abrirá a página do índice no browser.
Para obter mais informações sobre o Javadoc, consulte os recursos a seguir:
* link:http://java.sun.com/j2se/javadoc/[+Home Page Oficial da Ferramenta Javadoc+]
* link:http://java.sun.com/j2se/javadoc/writingdoccomments/index.html[+Como Escrever Comentários Doc na Ferramenta Javadoc+]
[start=10]
. Passe o mouse sobre a variável `session`. Observe que o depurador é suspenso na linha _está prestes a ser executado._ O valor retornado por `getSession()` ainda não foi salvo na variável de `session` e você verá uma popup informando que "` session` não é uma variável conhecida "no contexto atual".
image::images/session-variable.png[title="Passe o mouse sobre variáveis e expressões para determinar seus valores atuais"]
[start=11]
. Clique no botão Fazer Step Over (image::images/step-over-btn.png[]) na barra de ferramentas do depurador, localizada acima do editor. A linha será executada e o depurador passará para a próxima linha do arquivo.
[start=12]
. Passe novamente o mouse sobre a variável `session`. Agora você poderá ver o valor definido no momento para a variável `session`.
[.feature]
--
image::images/session-variable-set.png[role="left", link="images/session-variable-set.png"]
--
No NetBeans 6.9, você pode clicar no ponteiro cinza (image::images/grey-pointer.png[]) no popup para expandir uma lista de valores de variáveis contidos no elemento destacado.
[start=13]
. Clique o botão Fazer Step Over (image::images/step-over-btn.png[]) (F8; fn-F8 no Mac) para chegar à instrução `if` (linha 154). Como você acabou de clicar no botão "adicionar ao carrinho" no browser, deve sabe que a expressão `userPath.equals("/addToCart)` deverá ser avaliada como `true`.
[start=14]
. Realce a expressão `userPath.equals("/addToCart")` (clicando com o mouse enquanto mantém a tecla Ctrl pressionada). Desta vez você verá um popup que indica o valor da expressão que foi realçada.
image::images/expression.png[title="Destaque as expressões para determinar seus valores atuais"]
[start=15]
. Pressione F8 (fn-F8 no Mac) para passar para a próxima linha (linha 158). A aplicação foi criada para que o objeto `ShoppingCart` para a sessão do usuário seja criado somente quando o usuário adicionar um item ao carrinho pela primeira vez. Já que esta é a primeira vez que a solicitação `addToCart` foi recebida nesta sessão de depuração, pode-se esperar que o objeto `cart` seja igual a `null`.
image::images/cart-null.png[title="O objeto carrinho não existe, até que o usuário adicione item ao carrinho de compras"]
[start=16]
. Pressione F8 (fn-F8 no Mac) para passar à próxima linha (linha 160). Depois, na linha 160, onde o objeto `ShoppingCart` é criado, clique no botão Fazer Step Into (image::images/step-into-btn.png[]). O depurador entra no método que está sendo chamado. Neste caso, você será levado diretamente ao construtor do `ShoppingCart`.
image::images/cart-constructor.png[title="Fazer step into nos métodos para seguir a execução de runtime para outras classes"]
[start=17]
. Pressione Ctrl-Tab para voltar ao `ControllerServlet`. Observe que o IDE fornece um Emblema (image::images/call-stack-badge.png[]) de Pilha de Chamada na linha 160, indicando que o depurador está suspenso temporariamente em algum lugar em um método mais no início da pilha de chamada.
Pressione Alt-Shift-3 (Ctrl-Shift-3 no Mac) para abrir a janela Pilha de Chamada do IDE.
[start=18]
. Pressione F8 (fn-F8 no Mac) para continuar avançando pelo código. Quando o depurador conclui o construtor `ShoppingCart`, você é levado de volta ao `ControllerServlet`.
A linha 161 do `ControllerServlet` vincula o objeto `cart` recém-criado à sessão.
[source,java]
----
session.setAttribute("cart", cart);
----
Para testemunhar isso, abra a janela Variáveis do depurador. Escolha Janela > Depurando > Variáveis ou pressione Alt-Shift-1 (Ctrl-Shift-1 no Mac).
[.feature]
--
image::images/variables-win-session.png[role="left", link="images/variables-win-session.png"]
--
Se expandir o nó sessão > sessão > atributos, você poderá exibir os objetos que estão vinculados à sessão. Na imagem acima há dois itens vinculados atualmente à sessão (realçados). Eles são `selectedCategory` e `categoryProducts`, instanciados no `ControllerServlet`, nas linhas 83 e 89, respectivamente. Os dois itens foram vinculados anteriormente, quando você clicou na imagem de uma categoria, e o `ControllerServlet` processou a solicitação da página da categoria.
[start=19]
. Pressione F8 (fn-F8 no Mac) para executar a linha 161. O objeto `cart` será vinculado à sessão e a janela Variáveis será atualizada para exibir as alterações. Na janela Variáveis, note que agora a sessão contém três atributos, sendo o terceiro o objeto `ShoppingCart` recém-inicializado (realçado abaixo).
[.feature]
--
image::images/variables-win-session-cart.png[role="left", link="images/variables-win-session-cart.png"]
--
Até agora não "provamos" que a sessão, como listada na janela Variáveis, representa uma `HttpSession`. Como mencionado anteriormente, a `HttpSession` é na verdade uma interface, portanto, quando falamos sobre um objeto`HttpSession`, ou sobre um objeto de sessão, estamos nos referindo a qualquer objeto que implementa a interface `HttpSession`. Na janela Variáveis, se você posicionar o cursor sobre "`session`", um popup será exibido indicando que a variável representa um objeto `HttpSession`. O tipo `StandardSessionFacade`, como exibido, é a classe interna que o GlassFish utiliza para implementar a interface `HttpSession`. Se você estiver familiarizado com o Tomcat e estiver intrigado com os caminhos "`org.apache.catalina`" que aparecem na coluna Valor, é porque o contêiner web/servlet do GlassFish é, na verdade, um derivado do contêiner Apache Tomcat.
Um novo `ShoppingCart` é adicionado à sessão e a solicitação continua a ser processada. Para concluir a implementação da funcionalidade "adicionar ao carrinho", são realizadas as seguintes ações:
* o ID do produto selecionado é recuperado da solicitação (linha 165)
* um objeto `Product` é criado utilizando o ID (linha 169)
* um novo `ShoppingCartItem` é criado utilizando o `product` (linha 170)
* o `ShoppingCartItem` é adicionado à lista de `itens` do `ShoppingCart` (linha 170)
[start=20]
. Pressione F8 (fn-F8 no Mac) para continuar avançando pelo código, ao mesmo tempo ciente das quatro ações listadas acima. Pause quando o depurador for suspenso na linha 170.
[start=21]
. Crie um watch na sessão. Isso lhe permitirá exibir valores contidos na sessão ao fazer step into no método `addItem` na próxima etapa. Clique com o botão direito do mouse na sessão na janela Variáveis e selecione Criar Watch Fixo.
image::images/create-watch.png[title="Crie controles em variáveis, à medida que percorre o código em uma sessão de depuração"]
Como alternativa, você pode colocar o cursor na variável `session` no editor e, em seguida, clicar com o botão direito do mouse e selecionar Novo Watch. A caixa de diálogo Novo Watch permite que você especifique variáveis ou expressões para watch continuamente ao depurar uma aplicação. (No caso de expressões, realce primeiro a expressão e, em seguida, clique com o botão direito do mouse e selecione Novo Watch.)
image::images/new-watch-dialog.png[title="Clique com o botão direito do mouse nas variáveis e expressões no editor e escolha Novo Watch"]
Um novo watch será criado na variável `session` e em todas as variáveis que ela contiver. O watch é visível na janela Watches (Janela >Depuração >Watches) ou, se você alternar o botão Watches (image::images/watch-btn.png[]) na margem esquerda da janela Variáveis, ele será exibido na linha superior da janela Variáveis.
O depurador lhe permite ficar atento às variáveis enquanto percorre o código. Isso pode ser útil, por exemplo, se você quiser seguir as alterações de valores de variáveis específicas (e não quiser ter que analisar toda a lista apresentada na janela Variáveis em cada etapa) ou se fizer step into temporariamente em uma classe que não contenha as variáveis que lhe interessam.
[start=22]
. Clique na botão Fazer Step Into para (image::images/step-into-btn.png[]) fazer step into no método `addItem` do `ShoppingCart`.
[start=23]
. Percorra o método `addItem` até chegar na linha 53. Como o Javadoc atesta, `addItem` _"adiciona um `ShoppingCartItem` à lista de `items` do `ShoppingCart`. Se o item do `product` especificado já existe no carrinho de compra, a quantidade desse item é aumentada."_
[start=24]
. Examine a variável `session` na qual você criou um watch (<<step21,etapa 21>> acima). A instrução `items.add(scItem)` na linha 51 adicionou o novo `ShoppingCartItem` à lista de `items` no `ShoppingCart`. Isso fica evidente ao entrar no terceiro atributo (ex., a variável `cart`) contida na sessão.
[.feature]
--
image::images/variables-window-add-item.png[role="left", link="images/variables-window-add-item.png"]
--
Nesta etapa, você pode ver como uma `HttpSession` é criada para a solicitação, como um objeto `ShoppingCart` é criado e anexado à sessão e como um `ShoppingCartItem` é criado com base na escolha de produto do usuário e adicionado à lista de `Items` do `ShoppingCart`. A única ação remanescente é encaminhar a solicitação à view `category.jsp`.
[start=25]
. Abra o fragmento JSP do cabeçalho (`header.jsp`) no editor e coloque um ponto de interrupção na linha 86. Essa linha contém a instrução EL no widget do carrinho de compras que exibe o número de itens do carrinho.
image::images/breakpoint-jsp.png[title="Você pode suspender o depurador nas páginas JSP"]
[start=26]
. Clique no botão Continuar ( image::images/continue-btn.png[] ) na barra de ferramentas do depurador. O depurador continua agindo até que a execução seja concluída ou até atingir outro ponto de interrupção. Nesse caso, o depurador é suspenso na linha 86 no fragmento JSP do cabeçalho.
*Observação:* para suspender o depurador em uma página JSP, é necessário definir um ponto de interrupção. Por exemplo, quando o `ControllerServlet` encaminha a solicitação à view apropriada, o depurador não será suspenso automaticamente dentro da página JSP.
[start=27]
. Abra a janela Variáveis (Alt-Shift-1; Ctrl-Shift-1 no Mac), se ainda não estiver aberta. Diferente do que acontece com as classes Java, o depurador _não_ fornece dicas de ferramenta quando o mouse é posicionado sobre as variáveis ou expressões em uma página JSP. Entretanto, a janela Variáveis lhe permitirá determinar os valores das variáveis ao percorrer o código. Então, onde você pode encontrar o valor para `${cart.numberOfItems}`?
[start=28]
. Na janela Variáveis, amplie o nó Objetos implícitos > pageContext > sessão > sessão > atributos. Isso permite o acesso ao objeto de sessão, como o que foi visto anteriormente ao trabalhar com o `ControllerServlet`. Na verdade, você pode notar que a sessão na qual um watch foi criado na etapa 21 acima indica o mesmo objeto. Aqui você pode verificar se o valor de `${cart.numberOfItems}` é igual a "`1`".
[.feature]
--
image::images/variables-window-number-of-items.png[role="left", link="images/variables-window-number-of-items.png"]
--
Maximize a janela Variáveis, ou qualquer janela no IDE, clicando com o botão direito do mouse no cabeçalho da janela e, em seguida, selecionando Maximizar Janela (Shift-Esc).
O depurador lhe dá acesso ao objeto implícito `pageContext`. `pageContext` representa o contexto da página JSP e oferece acesso direto aos vários objetos, incluindo os objetos `HttpServletRequest`, `HttpSession` e `ServletContext`. Para obter mais informações, consulte o link:http://java.sun.com/javaee/5/docs/tutorial/doc/bnahq.html#bnaij[+Tutorial do Java EE 5: Objetos Implícitos+].
[start=29]
. Clique no botão Finalizar Sessão (image::images/finish-session-btn.png[]). O runtime finaliza a execução e a sessão de depuração é encerrada. O browser exibe uma página de categoria totalmente renderizada e você poderá ver que o widget carrinho de compras no cabeçalho da página contém um item.
Esperamos que agora você se sinta confortável ao utilizar o depurador do IDE, não somente para examinar seu projeto quando ele apresentar um comportamento inesperado, mas também como uma ferramenta que o ajuda a se familiarizar mais com o código. Outros botões úteis na barra de ferramentas do depurador incluem:
* (image::images/step-out.png[]) *Fazer Step Out:* você sai da chamada do método atual. Execute e remova o método de chamada superior da pilha de chamadas.
* (image::images/run-to-cursor.png[]) *Executar até o Cursor:* executa até a linha na qual o cursor está posicionado.
* (image::images/apply-code-changes.png[]) *Aplicar Alterações de Código:* depois de editar um arquivo, você pode pressionar este botão para que o arquivo seja recompilado e as alterações levadas em conta na sessão de depuração.
* (image::images/step-over-expression.png[]) *Fazer Step Over da Expressão:* permite que você exiba os parâmetros de entrada e os valores de saída resultantes de cada chamada de método dentro de uma expressão. Você pode inspecionar os valores de saída do método anterior e os parâmetros de entrada para o próximo método na janela Variáveis Locais. Quando não há mais nenhuma chamada de método, Fazer Step Over da Expressão se comporta como o comando Fazer Step Over (image::images/step-over-btn.png[]).
[[session-track]]
== Analisando as Opções de Rastreamento da Sessão
Há três maneiras convencionais de rastrear as sessões entre o cliente e o servidor. Com certeza a mais comum é com cookies. A reescrita de URL pode ser aplicada na ocasião em que os cookies não estiverem suportados ou desativados. Campos de forms ocultos também podem ser utilizados como uma maneira de "manter o estado" sobre várias solicitações, mas estão limitados ao uso dentro dos forms.
O projeto `AffableBean` inclui um exemplo do método de campo oculto nas duas páginas, da categorias e do carrinho. Os botões "adicionar ao carrinho" e "atualizar", que são exibidos para os itens de produtos, contêm um campo oculto que retransmite ID do produto para o servidor quando o botão é clicado. Se abrir a página `cart.jsp` no editor, você verá que as tags `<form>` contêm um campo oculto.
[source,xml]
----
<form action="updateCart" method="post">
*<input type="hidden"
name="productId"
value="${product.id}">*
...
</form>
----
Dessa forma, o ID do produto é enviado como um parâmetro de solicitação que o servidor utiliza para identificar o item dentro do carrinho do usuário, cuja quantidade precisa ser modificada.
A API do Servlet fornece um mecanismo de alto nível para o gerenciamento de sessões. Essencialmente, ela cria e passa um cookie entre o cliente e o servidor com cada ciclo de resposta da solicitação. Se o browser do cliente não aceitar cookies, o mecanismo do servlet será revertido automaticamente para a reescrita de URL. Os dois exercícios a seguir demonstram essa funcionalidade.
* <<http-monitor,Analisando a Comunicação Cliente-Servidor com o Monitor HTTP>>
* <<url-rewrite,Mantendo Sessões com a Reescrita de URL>>
[[http-monitor]]
=== Analisando a Comunicação Cliente-Servidor com o Monitor HTTP
Como default, o mecanismo do servlet utiliza cookies para manter e identificar sessões entre as solicitações. Um número alfanumérico aleatório é gerado para cada objeto de sessão, que serve como um identificador exclusivo. Esse identificador é passado como um cookie "`JSESSIONID`" ao cliente. Quando o cliente faz uma solicitação, o mecanismo do servlet lê o valor do cookie `JSESSIONID` para determinar a sessão à qual a solicitação pertence.
Para demonstrar isso, nós utilizaremos o depurador em conjunto com o monitor HTTP do IDE.
1. Comece com a ativação do monitor HTTP para o servidor que está utilizando. Selecione Ferramentas > Servidores. Na coluna à esquerda da janela Servidores, selecione o servidor que está utilizando (GlassFish). Em seguida, na coluna principal, selecione a opção Ativar Monitor HTTP.
image::images/servers-win-http-monitor.png[title="Selecione a opção Ativar Monitor HTTP para ativar o Monitor HTTP"]
[start=2]
. Se o servidor já estiver sendo executado, será necessário reiniciá-lo. No entanto, como pretende usar o depurador e executar o depurador reinicia o servidor para se comunicar em uma porta diferente, simplesmente clique no botão Depurar Projeto (image::images/debug-project-btn.png[]) no na barra de ferramentas principal do IDE. Quando o servidor é reiniciado, uma sessão de depuração é iniciada e a página de boas-vindas da aplicação é aberta no browser. O monitor HTTP é exibido na parte inferior do IDE.
image::images/http-monitor.png[title="O Monitor HTTP é exibido por default na região inferior do IDE"]
[start=3]
. Clique no registro AffableBean na coluna à esquerda (como mostrado na imagem acima). Quando você seleciona registros na coluna à esquerda, a coluna à direita (ex., principal) será atualizada para exibir os dados correspondentes. Na imagem acima, a guia Solicitação exibe o URI solicitado ( `/AffableBean/`), o método HTTP ( `GET`) e indica que nenhuma string de consulta foi enviada com a solicitação.
[start=4]
. Selecione a guia Sessão. Note que há uma instrução: "a sessão foi criada como resultado desta solicitação.&amp;quot Isso se deve ao fato de o servidor ter enviado um cabeçalho `Set-Cookie` para o cookie`JSESSIONID` na sua resposta. Observe também que o novo ID da sessão está listado em "Propriedades da sessão". Como será mostrado mais tarde, o ID da sessão é o valor do cookie `JSESSIONID`.
image::images/session-tab.png[title="Detalhes da sessão são exibidos na guia Sessão no Monitor HTTP"]
Talvez esteja pensando como um objeto de sessão foi criado de uma solicitação para a página de boas-vindas do site. Afinal, o `ControllerServlet` não trata a solicitação inicial para `/AffableBean/` e em nenhum lugar essa solicitação encontra `getSession()`. Ou encontra? Lembre-se de que as páginas JSP são compiladas em servlets na implantação. Depois de ter implantado o projeto no servidor, você poderá, na verdade, utilizar o IDE para exibir o servlet compilado do JSP no seu servidor.
[start=5]
. Na janela Projetos, clique com o botão direito do mouse no arquivo `index.jsp` e selecione Exibir Servlet. Será aberto um arquivo `index_jsp.java` no editor. Esse é o servlet que foi compilado automaticamente a partir da página `index.jsp`.
[start=6]
. Execute uma pesquisa no arquivo por `getSession`. Pressione Ctrl-F (⌘-F no Mac), digite "`getSession`" na barra de pesquisa e, em seguida, pressione Enter.
Ctrl-F (⌘-F no Mac) é um atalho do teclado para Editar > Localizar.
image::images/get-session.png[title="O método getSession existe no servidor compilado da página JSP"]
O método `getSession` foi, na verdade, chamado. A razão para isso é que as páginas JSP incluem, por default, o objeto implícito `pageContext.session`. Se quisesse desativar esse comportamento, você poderia ter adicionado a diretiva a seguir à parte superior de um arquivo JSP:
[source,java]
----
<%@page session="false" %>
----
e o método `getSession` no servlet compilado seria removido.
Para encontrar a localização do servlet compilado no servidor, você pode passar o mouse sobre a guia do nome do servlet acima do editor. Um popup exibe o caminho o caminho para o arquivo no computador.
[start=7]
. No browser, selecione uma categoria e, em seguida, adicione um item ao seu carrinho. Volte para o IDE. Observe que o depurador é suspenso no ponto de interrupção no `ControllerServlet` que foi definido anteriormente (linha 150). Todos os pontos de interrupção são lembrados entre as sessões. Para remover o ponto de interrupção, você poderia clicar no emblema do ponto de interrupção (image::images/breakpoint-badge.png[]) na margem esquerda do editor. No entanto, como há vários pontos de interrupção já definidos no projeto, abra a janela Pontos de interrupção do depurador (Janela > Depuração > Pontos de Interrupção).
image::images/breakpoints-window.png[title="Exibir todos os pontos de interrupção em seu projeto na janela Pontos de interrupção"]
Na janela Pontos de interrupção, você pode exibir chamar ações em todos os pontos de interrupção definidos nos projetos abertos no IDE.
[start=8]
. Clique com o botão direito do mouse no ponto de interrupção definido em `header.jspf` e selecione Deletar. Em seguida, clique com o botão direito do mouse no ponto de interrupção definido no `ControllerServlet` e selecione Desativar. (Você irá reabilitá-lo mais tarde neste exercício.)
[start=9]
. Clique no botão Continuar (image::images/continue-btn.png[]). A execução da solicitação é finalizada e a página da categoria será exibida no browser com um item adicionado ao carrinho.
[start=10]
. No Monitor HTTP, procure a solicitação `addToCart` na coluna esquerda e, em seguida, selecione-a para exibir os detalhes na coluna principal.
Clique no botão Classificação Ascendente (image::images/ascending-sort-btn.png[]) para que os registros mais recentes sejam listados na parte superior.
Na guia Solicitação, observe o URI solicitado (`/AffableBean/addToCart`), o método HTTP (`POST`) e os parâmetros da solicitação (`productId` e `submit`).
[.feature]
--
image::images/http-monitor-add-to-cart.png[role="left", link="images/http-monitor-add-to-cart.png"]
--
[start=11]
. Selecione a guia Cookies. Aqui você verá que existe um cookie chamado `JSESSIONID` e que foi enviado do cliente para o servidor. Observe que o valor para o cookie é igual ao do ID da Sessão exibido na guia Sessão.
image::images/cookies-tab.png[title="Cookies são exibidos na guia Cookies no Monitor HTTP"]
Da mesma forma, se clicar na guia Cabeçalho, verá o cookie listado, já que "`Cookie`" é um cabeçalho da solicitação enviado pelo cliente.
image::images/headers-tab.png[title="Cookies são exibidos na guia Cookies no Monitor HTTP"]
Consulte a link:http://en.wikipedia.org/wiki/List_of_HTTP_headers[+Lista de cabeçalhos HTTP+] da Wikipedia para obter mais informações sobre cabeçalhos de solicitações e de respostas.
[start=12]
. Selecione a guia Sessão. Há uma instrução que indica: "A sessão existiu antes desta solilcitação". Note também que o atributo `cart` é listado em "Atributos de sessão depois da solicitação". Isso faz sentido, já que sabemos que o objeto `cart` está vinculado à sessão quando a solilcitação `addToCart` é processada pela primeira vez.
image::images/session-tab-add-to-cart.png[title="Atributos de sessão são exibidos na guia Sessão no Monitor HTTP"]
Nas próximas etapas, localize o ID da sessão e o cookie `JSESSIONID` na janela Variáveis.
[start=13]
. Reative o ponto de interrupção definido anteriormente no `ControllerServlet`. Pressione Alt-Shift-5 (Ctrl-Shift-5 no Mac) para abrir a janela Pontos de Interrupção e, em seguida, clique na caixa de seleção ao lado da entrada do ponto de interrupção para reativá-lo.
[start=14]
. No browser, clique no botão "adicionar ao carrinho" para um dos produtos listados.
[start=15]
. Alterne para o IDE e note que o depurador está suspenso no ponto de interrupção definido no `ControllerServlet`. Clique no botão Fazer Step Over (image::images/step-over-btn.png[]) para que a variável `session` seja atribuída ao objeto da sessão.
[start=16]
. Abra a janela Variáveis (Alt-Shift-1; Ctrl-Shift-1 no Mac) e expanda sessão > sessão. Você encontrará o ID da sessão listado como o valor para a variável `id`.
[start=17]
. Para localizar o cookie `JSESSIONID`, lembre-se de que você pode acessar normalmente os cookies de um servlet chamando o método link:http://java.sun.com/webservices/docs/1.6/api/javax/servlet/http/HttpServletRequest.html#getCookies%28%29[+`getCookies`+] no `HttpServletRequest`. Portanto, entre no objeto da solicitação: solicitação > Herdado > solicitação > solicitação > Herdado > cookies. Aqui você pode ver a ArrayList `cookies`. Se expandir a lista, encontrará o cookie `JSESSIONID`, cujo valor é o ID da sessão.
[start=18]
. Clique no botão Finalizar Sessão (image::images/finish-session-btn.png[]) para encerrar a sessão de depuração.
[[url-rewrite]]
=== Mantendo Sessões com a Reescrita de URL
Como mencionado, o mecanismo do servlet detecta se os cookies são suportados para o browser do cliente e, caso não sejam, ele alterna para a reescrita de URL, como uma forma de manter as sessões. Isso tudo ocorre de um modo transparente para o cliente. Para o desenvolvedor, o processo não é totalmente transparente.
É necessário certificar-se de que a aplicação pode reescrever os URLs, caso os cookies sejam desativados. Você faz isso chamando o método `encodeURL` de resposta em todos os URLs retornados por servlets em sua aplicação. Fazer isso permite que o ID da sessão seja acrescentado ao URL, caso o uso de cookies não seja uma opção; caso contrário, será retornado o URL sem alteração.
Por exemplo, o browser envia uma solicitação para a terceira categoria (padaria) do `AffableBean``category?3`. O servidor responde com o ID de sessão incluído no URL:
[source,java]
----
/AffableBean/category*;jsessionid=364b636d75d90a6e4d0085119990*?3
----
Como foi declarado acima, _todos os URLs retornados pelos servlets da aplicação devem estar codificados_. Lembre-se de que as páginas JSP são compiladas em servlets. Como é possível codificar os URLs em páginas JSP? A tag link:http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/c/url.html[+`<c:url>`+] do JSTL serve para esse propósito. O exercício a seguir demonstra o problema e ilustra a solução.
1. Desative os cookies temporariamente no browser. Se estiver utilizando o Firefox, poderá selecionar Ferramentas > Opções (Firefox > Preferências no Mac). Na janela exibida, selecione a guia Privacidade e, em seguida, em Histórico, selecione "Utilizar definições personalizadas para o histórico" na lista drop-down fornecida. Desmarque a opção "Aceitar cookies de sites".
image::images/firefox.png[title="Desative os cookies temporariamente no browser."]
[start=2]
. Execute o projeto `AffableBean`. Quando a página de boas-vindas for exibida, clique em uma categoria e, em seguida, tente adicionar um item ao seu carrinho. Você verá que a funcionalidade da aplicação está severamente comprometida no seu estado atual.
image::images/compromised.png[title="A funcionalidade da aplicação fica comprometida quando o cliente não aceita cookies"]
Como antes, o servidor gera uma sessão e vincula objetos a ela. Isso mostra como a página da categoria pode exibir a categoria e os produtos selecionados. Entretanto, o servidor falhou na sua tentativa de definir um cookie `JSESSIONID`. Portanto, quando o cliente faz uma segunda solicitação (quando o usuário clica em "adicionar ao carrinho"), o servidor não tem como identificar a sessão à qual a solicitação pertence. Portanto, ele não pode localizar nenhum dos atributos definidos anteriormente na sessão, como `selectedCategory` e `categoryProducts`. Essa é a razão pela qual falta informações na resposta renderizada especificada por esses atributos.
[start=3]
. Abra a página `category.jsp` do projeto no editor. Localize a linha que implementa o botão "adicionar ao carrinho" (linha 58). O atributo `<form>` do elemento `action` determina a solicitação enviada ao servidor.
[source,java]
----
<form action="addToCart" method="post">
----
[start=4]
. Modifique a solicitação de forma que seja passada pela guia `<c.url>`.
[source,java]
----
<form action="*<c:url value='addToCart'/>*" method="post">
----
[start=5]
. Pressione Ctrl-S (⌘-S no Mac) para salvar as alterações do arquivo. Lembre-se de que o IDE fornece a funcionalidade Implantar ao Salvar, que é ativada por default. Isso significa que quaisquer alterações salvas são implantadas automaticamente no servidor.
[start=6]
. No browser, selecione uma categoria diferente para que a aplicação renderize a página da categoria modificada recentemente.
[start=7]
. Examine o código-fonte da página. No Firefox, você pode pressionar Ctrl-U (⌘-U no Mac). O botão "adicionar ao carrinho" de cada produto é exibido com o ID da sessão acrescentado ao URL.
[source,java]
----
<form action="addToCart*;jsessionid=4188657e21d72f364e0782136dde*" method="post">
----
[start=8]
. Clique no botão "adicionar ao carrinho" de qualquer item. Você verá que o servidor agora pode determinar a sessão à qual a solicitação pertence e de renderizar a resposta de forma adequada.
[start=9]
. Antes de prosseguir, certifique-se de reativar os cookies no browser.
Novamente, cada link em que o usuário pode clicar na aplicação, cuja resposta exija alguma forma de dados relacionados à sessão, precisa ser codificado corretamente. Às vezes a implementação não é direta, como o exemplo mostrado acima. Por exemplo, o widget "limpar carrinho" utilizado no `cart.jsp` define atualmente um parâmetro `clear` como `true` quando o link é clicado.
[source,xml]
----
<%-- clear cart widget --%>
<c:if test="${!empty cart &amp;&amp; cart.numberOfItems != 0}">
<a href="viewCart*?clear=true*" class="bubble hMargin">clear cart</a>
</c:if>
----
A tag `<c.url>` pode ser aplicada ao URL da seguinte forma:
[source,xml]
----
<%-- clear cart widget --%>
<c:if test="${!empty cart &amp;&amp; cart.numberOfItems != 0}">
*<c:url var="url" value="viewCart">
<c:param name="clear" value="true"/>
</c:url>*
<a href="*${url}*" class="bubble hMargin">clear cart</a>
</c:if>
----
O parâmetro `clear=true` é definido quando uma tag `<c:param` é adicionada entre as tags `<c.url>`. Uma variável chamada "`url`" é definida quando o atributo `var` de <c.url> é utilizado e `var` é então acessado na tag âncora HTML utilizando a expressão `${url}`.
É possível fazer download e examinar o link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252Fecommerce%252FAffableBean_snapshot6.zip[+snapshot 6+] para ver como todos os links no projeto foram codificados.
A reescrita de URL só deve ser utilizada no caso dos cookies não serem um método de rastreamento disponível. A reescrita de URL é geralmente considerada uma solução de qualidade inferior porque ela expõe o ID da sessão nos logs, marcadores, cabeçalhos de referência e HTML armazenados no cache, além da barra de endereço do browser. São necessários também mais recursos do lado servidor, pois o servidor precisa executar etapas adicionais para cada solicitação de entrada, para extrair o ID da sessão do URL e emparelhá-lo com uma sessão existente.
[[time-out]]
== Tratando Time-Outs de Sessão
* <<time-interval,Definindo Intervalos de Tempo da Sessão>>
* <<programmatically,Tratando de Forma Programática Time-outs de Sessão>>
[[time-interval]]
=== Definindo Intervalos de Tempo da Sessão
É necessário considerar o intervalo de tempo máximo no qual o servidor mantém as sessões. No caso de o website receber um tráfego intenso, um grande número de sessões poderia usar toda a capacidade de memória do servidor. É necessário, portanto, encurtar o intervalo no intuito de remover as sessões não utilizadas. Por outro lado, você certamente não desejaria encurtar muito as sessões, já que isso poderia se tornar um problema de uso que poderia ter impacto negativo nos negócios ligados ao website. Pegando a aplicação `AffableBean` como exemplo, uma usuária faz o check-out após encher seu carrinho de compras com itens. Ela então percebe que precisa inserir os detalhes do cartão de crédito e sai para pegar a carteira. Depois de retornar ao computador com o cartão de crédito em mãos, preenche o form de check-out e clica em submeter. No entanto, durante esse tempo, sua sessão foi expirada no servidor. A usuária nota que seu carrinho de compras está vazio e é redirecionada à home page. Será que ela realmente irá passar por todo o processo novamente?
As etapas a seguir demonstram como definir o intervalo de time-out de sessão no projeto `AffableBean` para 10 minutos. Claro que a duração real depende, em última análise, dos recursos do servidor, dos objetivos de negócios da aplicação e da popularidade do seu website.
1. Abra o descritor de implantação da aplicação no editor. Pressione Alt-Shift-O (Ctrl-Shift-O no Mac) para utilizar a caixa de diálogo do IDE Ir para Arquivo. Digite "`web`" e, em seguida, clique em OK.
image::images/go-to-file.png[title="A caixa de diálogo Ir para Arquivo permite navegação rápida para os arquivos do projeto"]
O editor exibirá o arquivo `web.xml` na view XML. O modelo que o NetBeans fornece para o arquivo `web.xml` inclui uma definição default de 30 minutos.
[source,xml]
----
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
----
[start=2]
. Clique na guia Geral e digite "`10`" no campo Time-out de Sessão.
image::images/session-timeout.png[title="Especifique o time-out de sessão para a aplicação na guia Geral do web.xml"]
[start=3]
. Salve o arquivo (Ctrl-S; ⌘-S no Mac).
Se voltar para a view XML, verá que o elemento `<session-timeout>` foi atualizado.
[source,xml]
----
<session-config>
<session-timeout>10</session-timeout>
</session-config>
----
*Observação:* como alternativa, você pode remover completamente o elemento `<session-timeout>` e editar o elemento `session-properties` no descritor de implantação específico do GlassFish (`sun-web.xml`). Dessa forma, você definiria o time-out global para todas as aplicações no módulo web do servidor. Consulte o link:http://docs.sun.com/app/docs/doc/821-1752/beaha[+Oracle GlassFish Server 3.0.1 Application Development Guide: Creating and Managing Sessions+] para obter mais detalhes.
[[programmatically]]
=== Tratando de Forma Programática Time-outs de Sessão
Se a sua aplicação se basear em sessões, será necessário tomar medidas para garantir que ela possa tratar, de maneira primorosa, situações nas quais uma solicitação é recebida para uma sessão que teve time-out ou que não pode ser identificada. Você pode realizar isso na aplicação `AffableBean` criando um filtro simples que intercepta as solicitações enviando-as para o `ControllerServlet`. O filtro verifica se a sessão existe, se não existir, ele encaminhará a solicitação à página de boas-vindas do site.
1. Comece examinando o problema que aparece quando há um time-out da sessão durante uma visita do usuário ao site. Redefina temporariamente o intervalo de time-out da sessão para um minuto. Abra o descritor de implantação web (`web.xml`) e insira "`1`" entre as tags `<session-timeout>`.
[source,xml]
----
<session-config>
<session-timeout>*1*</session-timeout>
</session-config>
----
[start=2]
. Execute o projeto `AffableBean`. No browser, clique na página da categoria, adicione vários itens ao carrinho e, em seguida, clique em "ver carrinho".
image::images/cart-page-session-intact.png[title="A página do carrinho depende de um objeto da sessão para exibir itens no carrinho de compras"]
[start=3]
. Aguarde pelo menos um minuto completo.
[start=4]
. Atualize a quantidade de um dos itens exibidos na página do carrinho. (Qualquer número entre 1 e 99 é aceitável.) Clique em "atualizar". O servidor retorna uma mensagem HTTP Status 500.
image::images/glassfish-error-report.png[title="NullPointerException ocorre quando uma solicitação de sessão expirada é recebida"]
[start=5]
. Examine o log do GlassFish Server no IDE. Abra a janela Saída (Ctrl-4, ⌘-4 no Mac) e selecione a guia GlassFish Server. Role para a parte inferior do log para examinar o rastreamento da pilha de erro.
[.feature]
--
image::images/gf-server-output.png[role="left", link="images/gf-server-output.png"]
--
O log do servidor indica que uma `NullPointerException` ocorreu na linha 184 no `ControllerServlet`. A janela Saída forma um link para a linha em que ocorreu a exceção.
[start=6]
. Clique no link. Você pode navegar diretamente até a linha 184 no `ControllerServLet`. Passar o mouse sobre o emblema do erro na margem esquerda do editor fornece uma dica de ferramenta que descreve a exceção.
image::images/nullpointer-exception.png[title="O emblema do erro e a dica de ferramenta indicam a localização e a causa do problema"]
Como a sessão já havia expirado antes de a solicitação ter sido recebida, o mecanismo do servlet não poderá associar a solicitação à sua sessão correspondente. Portanto, ele não conseguiu localizar o objeto `cart` (linha 151). A exceção ocorreu finalmente na linha 184 quando o mecanismo tentou chamar um método em uma variável equivalente a `null`.
Agora que identificamos o problema, vamos corrigi-lo implementando um filtro.
[start=7]
. Clique no botão Novo Arquivo (image::images/new-file-btn.png[]) na barra de ferramentas do IDE. (Como alternativa, pressione Ctrl-N; ⌘-N no Mac.)
[start=8]
. Selecione a categoria *Web*e, em seguida, selecione *Filtro* e clique em Próximo.
[start=9]
. Nomeie o filtro`SessionTimeoutFilter`. Digite `filter` no campo Pacotes, para que a classe do filtro seja colocada em um novo pacote quando for criada.
[start=10]
. Clique em Próximo. Aceite as definições default e clique em Finalizar. Um modelo para o `SessionTimeoutFilter` será gerado e aberto no editor.
*Observação:* atualmente, no NetBeans 6.9, não é possível utilizar o assistente para definir um mapeamento para um servlet que não esteja registrado no descritor de implantação da web. (O `ControllerServlet` foi registrado usando a anotação `@WebServlet`.) Portanto, modificaremos o código gerado na próxima etapa.
[start=11]
. Modifique a assinatura da anotação `@WebFilter`, de maneira que apareça da seguinte forma.
[source,java]
----
@WebFilter(*servletNames = {"Controller"}*)
public class SessionTimeoutFilter implements Filter {
----
Isso define o filtro para interceptar qualquer solicitação que seja tratada pelo `ControllerServlet`. (Como alternativa, você poderia ter mantido o atributo `urlPatterns` e listado todos os padrões que o `ControllerServlet` trata.)
Note que "`Controller`" é o nome do `ControllerServlet`, como especificado na assinatura da anotação `@WebServlet` do servlet. Note também que o atributo `filterName` foi removido, já que o nome da classe do filtro já foi utilizado por default.
O modelo do filtro do IDE fornece vários códigos interessantes que valem a pena inspecionar por si só. No entanto, a maioria deles não é necessária para o nosso propósito. Qualquer classe de filtro deve implementar a interface `Filter`, que define três métodos:
* *`init`:* executa qualquer ação após o filtro ter sido inicializado, mas antes de ele ser colocado em serviço
* *`destroy`:* remove o filtro do serviço. Esse método também pode ser utilizado para executar qualquer operação de limpeza.
* *`doFilter`:* utilizado para executar operações para cada solicitação que o filtro interceptar
Utilize a opção Pesquisa Javadoc por Índice para puxar a documentação na interface `Filter`. Pressione Shift-F1 (fn-Shift-F1 no Mac) e, em seguida, digite "`Filter`" no campo de pesquisa e pressione Enter. Selecione a entrada "Interface no javax.servlet". A documentação Javadoc é exibida no painel inferior da ferramenta de pesquisa em índice.
[start=12]
. Substitua o corpo do `SessionTimeoutFilter` pelo conteúdo a seguir.
[source,java]
----
@WebFilter(servletNames = {"Controller"})
public class SessionTimeoutFilter implements Filter {
*public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession(false);
// if session doesn't exist, forward user to welcome page
if (session == null) {
try {
req.getRequestDispatcher("/index.jsp").forward(request, response);
} catch (Exception ex) {
ex.printStackTrace();
}
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {}
public void destroy() {}*
}
----
[start=13]
. Pressione Ctrl-Shift-I (⌘-Shift-I no Mac) para corrigir instruções de importação. (As importações precisam ser adicionadas a `HttpServletRequest` e `HttpSession`.) Além disso, use as dicas do editor para adicionar a anotação `@Override` aos métodos `init`, `destroy` e `doFilter`.
Nas etapas a seguir, você executará o depurador no projeto e percorrerá o método `doFilter` para ver como ele determina se a solicitação está vinculada a uma sessão existente.
[start=14]
. Abra a janela Pontos de Interrupção (Alt-Shift-5; Ctrl-Shift-5 no Mac) e verifique se não há nenhum ponto de interrupção existente definido. Para deletar um ponto de interrupção, clique com o botão direito do mouse no ponto de interrupção e selecione Deletar. (Se tiver concluído o exercício acima, <<http-monitor,Examinando a Comunicação Cliente-Servidor com o Monitor de HTTP>>, poderá ter um ponto de verificação pendente definido no `ControllerServlet`.)
[start=15]
. Execute o depurador. Clique no botão Depurar Projeto (image::images/debug-project-btn.png[]) na barra de ferramentas principal do IDE.
[start=16]
. Quando a página de boas-vindas for exibida no browser, selecione uma categoria e, em seguida, adicione vários itens ao seu carrinho de compras.
[start=17]
. Defina um ponto de interrupção na linha no método `` doFilter` do `SessionTimeoutFilter `` que tenta acessar a sessão (linha 32).
image::images/filter-breakpoint.png[title="Definir um ponto de interrupção no método getSession"]
[start=18]
. No browser, clique no botão "ver carrinho". Alterne para o IDE e note que o depurador foi suspenso no ponto de interrupção.
Lembre-se de que `getSession()` cria um novo objeto de sessão, caso o atual não exista. Aqui, utilizamos o `getSession(false)`, que se abstém de criar um novo objeto, caso não seja encontrado nenhum. Em outras palavras, o método retorna `null`, se a sessão não existir.
[start=19]
. Clique no botão Fazer Step Over (image::images/step-over-btn.png[]) e, em seguida, passe o ponteiro do mouse sobre a variável `session`. Contanto que não tenha se passado um minuto desde que a solicitação anterior foi enviada, você verá que a variável foi designada a `StandardSessionFacade`. Isso representa o objeto de sessão para a solicitação.
image::images/session-exists.png[title="Passe o mouse sobre as variáveis para determinar seus valores atuais"]
[start=20]
. Continue percorrendo o método, até que a solicitação seja processada. Como `session` não é igual a `null`, ignore a instrução `if` e `chain.doFilter` e, em seguida, encaminhe a solicitação ao `ControllerServlet` (linha 44).
[start=21]
. No browser, certifique-se de que se passou um minuto completo e, em seguida, atualize a quantidade de um dos itens de produtos no carrinho. Esse é o mesmo procedimento que executamos anteriormente no exercício, quando foi retornada a mensagem status 500. Agora que o filtro intercepta as solicitações direcionadas ao `ControllerServlet`, veremos o que acontece quando ocorrer o time-out da sessão.
[start=22]
. Depois de clicar em "atualizar", alterne para o IDE e note que o depurador é suspenso novamente no ponto de interrupção definido no filtro.
[start=23]
. Realce a expressão `req.getSession(false)` e, em seguida, passe o mouse sobre ela. Aqui você verá que a expressão equivale a `null`, já que a sessão já expirou.
image::images/session-null.png[title="Realce as expressões e passe o mouse sobre elas para determinar seu valor atual"]
[start=24]
. Continue percorrendo o código. Agora que a variável `session` equivale a `null`, a instrução `if` na linha 35 será processada e a solicitação será encaminhada para `/index.jsp`. Quando o depurador finalizar a execução, você verá que o browser exibirá a página de boas-vindas do site.
[start=25]
. Clique no botão Finalizar Sessão (image::images/finish-session-btn.png[]) para encerrar a sessão de depuração.
[start=26]
. Abra o arquivo `web.xml` do projeto e mude o intervalo de time-out da sessão de volta para 10 minutos.
[source,xml]
----
<session-config>
<session-timeout>*10*</session-timeout>
</session-config>
----
[start=27]
. Salve o arquivo (Ctrl-S; ⌘-S no Mac).
O link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252Fecommerce%252FAffableBean_snapshot6.zip[+Snapshot 6+] fornece a versão completa do projeto para esta unidade do tutorial. Um tópico final com relação ao gerenciamento de sessão deve ser mencionado. É possível encerrar explicitamente uma sessão chamando o método `invalidate` no objeto de sessão. Caso a sessão não seja mais necessária, ela deve ser removida para conservar a memória disponível para o servidor. Depois de concluir a próxima unidade, link:transaction.html[+Integrando Lógica de Negócio Transacional+], você verá como o `ControllerServlet`, ao processar com sucesso um pedido do cliente, destrói o objeto `cart` do usuário e encerra a sessão usando o método `invalidate`.
[source,java]
----
// if order processed successfully send user to confirmation page
if (orderId != 0) {
// dissociate shopping cart from session
cart = null;
// end session
session.invalidate();
...
}
----
Isso é demonstrado no link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252Fecommerce%252FAffableBean_snapshot8.zip[+snapshot 8 do projeto+] (e em snapshots posteriores).
link:/about/contact_form.html?to=3&subject=Feedback: NetBeans E-commerce Tutorial - Managing Sessions[+Envie-nos Seu Feedback+]
[[seeAlso]]
== Consulte Também
=== Recursos do NetBeans
* link:../../../../features/java/debugger.html[+Funcionalidades do NetBeans: Depurador+]
* link:../../java/debug-multithreaded.html[+Depurando Aplicações Multithread+]
* link:../../java/debug-multithreaded-screencast.html[+Vídeo de Depuração Multithread no NetBeans IDE+]
* link:../../java/debug-evaluator-screencast.html[+Vídeo sobre o Uso do Avaliador de Snippets de Código no Depurador do NetBeans+]
* link:../../screencasts.html[+Tutoriais e Demonstrações em Vídeo para NetBeans IDE 6.x+]
* link:https://netbeans.org/projects/www/downloads/download/shortcuts.pdf[+Atalhos de Teclado e Cartão de Modelos de Código+]
* link:../javaee-gettingstarted.html[+Introdução às Aplicações do Java EE 6+]
* link:../javaee-intro.html[+Introdução à Tecnologia Java EE+]
* link:../../../trails/java-ee.html[+Trilha do Aprendizado do Java EE e Java Web+]
=== Recursos do GlassFish
* link:http://wiki.glassfish.java.net/Wiki.jsp?page=Screencasts[+Screencasts do GlassFish+]
* link:https://glassfish.dev.java.net/docs/index.html[+Documentação do GlassFish v3+]
* link:http://www.sun.com/offers/details/GlassFish_Tomcat.html[+Aprendendo GlassFish para Usuários do Tomcat+]
* link:http://docs.sun.com/app/docs/doc/821-1751[+Guia de Administração do Oracle GlassFish Server 3.0.1+]
* link:http://docs.sun.com/app/docs/doc/821-1750[+Guia de Implantação de Aplicações do Oracle GlassFish Server 3.0.1+]
* link:http://docs.sun.com/app/docs/doc/821-1752[+Guia de Desenvolvimento de Aplicações do Oracle GlassFish Server 3.0.1+]
=== Artigos Técnicos &amp; Recursos Variados
* link:http://java.sun.com/javaee/reference/code/[+Amostras de Código do Java EE &amp; Aplicações+]
* link:http://java.sun.com/j2se/javadoc/[+Ferramenta Javadoc+] [home page do produto]
* link:http://java.sun.com/j2se/javadoc/writingdoccomments/index.html[+Como Escrever Comentários Doc na Ferramenta Javadoc+]
* link:http://java.sun.com/products/servlet/Filters.html[+Os Fundamentos dos Filtros+]
* link:http://java.sun.com/blueprints/corej2eepatterns/Patterns/InterceptingFilter.html[+Padrões Core J2EE: Interceptando Filtro+]
* link:http://courses.coreservlets.com/Course-Materials/csajsp2.html[+Tutoriais do Servlet, do JSP e do JDBC para os Níveis Iniciante e Intermediário+]
* link:http://courses.coreservlets.com/Course-Materials/msajsp.html[+Tutoriais Avançados do Servlet e do JSP+]
* link:http://courses.coreservlets.com/Course-Materials/java5.html[+Tutoriais do Java 5 &amp; do Java 6+]
* link:http://www.ibm.com/developerworks/java/library/j-jstl0211.html[+Uma cartilha JSTL, Parte 1: A linguagem da expressão+]
* link:http://www.ibm.com/developerworks/java/library/j-jstl0318/index.html[+Uma cartilha JSTL, Parte 2: Chegando ao centro do problema+]