Mostrando postagens com marcador scwcd. Mostrar todas as postagens
Mostrando postagens com marcador scwcd. Mostrar todas as postagens

quarta-feira, 25 de abril de 2007

Como criar funções em sua taglib

Caso você tenha necessidade de uma função que não existe na JSTL, você pode criar facilmente sua própria função. Nesse mini tutorial explicarei como fazer isto. Faremos uma função que retira os simbolos de maior e de menor (<>) dos textos para impedir que tags htmls sejam inseridas numa página. A primeira coisa a se fazer é criar uma classe com um método estático com essa funcionalidade. Veja:
package meupacote;

public class MinhaClasse
{
public static String escapeHtml(String html)
{
String texto = null;
if( html != null )
{
texto = html.replaceAll("<", "& lt;");
texto = texto.replaceAll(">", "& gt;");
}
return texto;
}
}
Como podemos ver, o método escapeHtml recebe um html e substitui todas as ocorrências de < > pelo seu código equivalente em html. O segundo passo é definir no seu arquivo tld essa function, inserindo a tag function e suas sub-tags de configuração. Veja:

<function>
<description>
Substitui tags simbolos de maior e maior pelo seu codigo html
</description>
<name>escapeHtml</name>
<function-class>meupacote.MinhaClasse</function-class>
<function-signature>boolean escapeHtml(java.lang.String)</function-signature>
</function>
As tags mais importantes da configuração da função são "name", que indica por qual nome ela será referenciada no jsp, "function-class", que indica qual classe possui o método equivalente à função e "funtion-signature", que indica qual o método responderá pelas ações da função.

Tendo isso, basta utilizar essa função no seu jsp, importando sua taglib e chamando a função no formato prefixo:nomefuncao(parametros). Veja o exemplo:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="mt" uri="http://programandosemcafeina.blogspot.com/jsp/taglib/mt" %>

<h1><c:out value="${mt:escapeHtml(materia.titulo)}" /></h1>
<p><c:out value="${mt:escapeHtml(materia.texto)}" /></p>
É claro que por eu usar a tag c:out não seria necessária a função escapeHtml, pois essa tag já faz a conversão que implementamos (e muitas outras). Contudo existem situações em que ela seria necessária. Um exemplo é se utilizarmos a Json Taglib. Essa taglib não tem a opção de escapar o html, para ela então faz-se necessário o uso de uma função como a que fizemos no exemplo.

segunda-feira, 16 de abril de 2007

Pra que serve e como funciona o ServletRequestAttributeListener

O ServletRequestAttributeListener é uma interface que serve para monitorar os atributos adicionados, alterados e removidos de uma requisição. Sempre que se adiciona um atributo no objeto ServletRequest, o método attributeAdded do listener que implementa essa interface é chamado. Sempre que se substitui um atributo do objeto ServletRequest, o método attributeReplaced do listener que implementa essa interface é chamado. E finalmente, sempre que se remove um atributo do objeto ServletRequest o método attributeRemoved é chamado.

Veja abaixo o exemplo de uma classe que implementa esse listener para exibir o nome e o valor do parâmetro que foi adicionado, alterado e removido:
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.http.HttpServletRequest;

public class MeuServletRequestAttributeListener implements ServletRequestAttributeListener
{
public void attributeAdded(ServletRequestAttributeEvent e)
{
System.out.println("Atributo adicionado -> nome: "
+ e.getName() + ", valor: " + e.getValue() );
}

public void attributeRemoved(ServletRequestAttributeEvent e)
{
System.out.println("Atributo removido -> nome: "
+ e.getName() + ", valor: " + e.getValue() );
}

public void attributeReplaced(ServletRequestAttributeEvent e)
{
System.out.println("Atributo alterado -> nome: "
+ e.getName() + ", valorAntigo: " + e.getValue() );
}
}

Para testar esse listener criei ainda um Servlet mapeado com o endereço /TesteAtributoRequest.do. Este Servlet faz forward para si mesmo decrementando o atributo "numero" que é guardado na requisição. Veja como fica o código:
public void doGet(HttpServletRequest request, HttpServletResponse response) 
throws IOException, ServletException
{
int numero;

if( request.getAttribute("numero") != null )
{
numero = ( (Integer)request.getAttribute("numero") ).intValue();
numero--;
}
else
{
try
{
numero = Integer.parseInt( request.getParameter("numero") );
}
catch(NumberFormatException e)
{
numero = 3;
}
}

if( numero <= 0 )
{
request.removeAttribute("numero");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("Acaboooou");
}
else
{
request.setAttribute("numero", new Integer(numero));
request.getRequestDispatcher( "/TesteAtributoRequest.do" ).forward(request,response);
}
}

Ao executá-lo mandando o parâmetro "numero" com valor igual a 5 por exemplo, o listener configurado irá exibir a seguinte saída:

Atributo adicionado -> nome: numero, valor: 5
Atributo alterado -> nome: numero, valorAntigo: 5
Atributo alterado -> nome: numero, valorAntigo: 4
Atributo alterado -> nome: numero, valorAntigo: 3
Atributo alterado -> nome: numero, valorAntigo: 2
Atributo removido -> nome: numero, valor: 1

Pra que serve e como funciona o ServletRequestListener

O ServletRequestListener serve para ser notificado quando uma aplicação recebe uma requisição e quando esta requisição é destruída. Nomea-se no caso uma requisição como um objeto ServletRequest. Assim que o objeto ServletRequest é inicializado o método requestInitialized do listener que implementa a interface ServletRequestListener é chamado. Quando o objeto ServletRequest é destruído, o método requestDestroyed é chamado.

Veja um exemplo de um listener que implementa ServletRequestListener abaixo:
import javax.servlet.ServletRequestListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.http.HttpServletRequest;

public class MeuServletRequestListener implements ServletRequestListener
{
public void requestInitialized(ServletRequestEvent e)
{
HttpServletRequest r = (HttpServletRequest)e.getServletRequest();
System.out.println("Requisicao inicializada: " +
r.getRequestURI() );
}

public void requestDestroyed(ServletRequestEvent e)
{
HttpServletRequest r = (HttpServletRequest)e.getServletRequest();
System.out.println("Requisicao finalizada: " +
r.getRequestURI() );
}
}

No caso, o que o listener faz é escrever na saída padrão a URI requisitada quando uma nova requisição chega à aplicação e quando a requisição é finalizada. É importante notar que mesmo quando é feito uma requisição cujo resultado é um 404, o objeto de requisição é criado e em consequência os métodos do listener são chamados.

quarta-feira, 11 de abril de 2007

Pra que serve e como funciona o ServletContextAttributeListener?

O ServletContextAttributeListener é um listener que recebe sinais quando um atributo é adicionado, removido ou alterado no contexto. Para quem não sabe, um atributo de contexto é válido para todos os servlets, threads e sessões de uma aplicação. Para testar seu funcionamento criei uma classe implementando a interface ServletContextAttributeListener e um servlet para adicionar, remover e alterar atributos do contexto.

Segue o código do listener:
package meupacote.listeners;

import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextAttributeEvent;

public class MeuServletContextAttributeListener implements ServletContextAttributeListener
{
public void attributeAdded(ServletContextAttributeEvent e)
{
System.out.println("Atributo adicionado: nome=" +
e.getName() + ", valor=" + e.getValue()
);
}

public void attributeRemoved(ServletContextAttributeEvent e)
{
System.out.println("Atributo removido: nome=" +
e.getName() + ", valor=" + e.getValue()
);
}

public void attributeReplaced(ServletContextAttributeEvent e)
{
System.out.println("Atributo alterado: nome=" +
e.getName() + ", valorAntigo=" + e.getValue()
);
}
}
Veja que para implementar esse listener é preciso sobrescrever os métodos attributeAdded, que é chamado quando um atributo é adicionado, attributeRemoved, que é chamado quando um atributo é removido e attributeReplaced que é chamado quando um atributo é alterado.

O evento que esses métodos recebem possue dois métodos: getName e getValue, eles servem para pegar o nome e o valor do atributo manejado na ação. É preciso observar que no attributeReplaced, o getValue do evento retorna o valor anterior ao que foi alterado.

Agora veja o código do servlet que inclui, altera e remove atributos do contexto:
public class MudaAtributoContexto extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
{
String acao = request.getParameter("acao");
String nome = request.getParameter("nome");
String valor = request.getParameter("valor");

response.setContentType("text/html");
PrintWriter out = response.getWriter();

if( nome!=null )
{
if( "remover".equals(acao) )
{
getServletContext().removeAttribute(nome);
out.print("Removido: " + nome);
}
else
{
getServletContext().setAttribute(nome, valor);
out.print("Setado: " + nome + " = " + valor);
}
}

StringBuilder s = new StringBuilder("<form><b>Incluir: </b>")
.append(" nome: <input name=\"nome\"/>")
.append(" valor: <input name=\"valor\"/>")
.append(" <input type=\"submit\"/>")
.append("</form><hr />");

Enumeration e = getServletContext().getAttributeNames();
while( e.hasMoreElements() )
{
String n = (String)e.nextElement();
Object v = getServletContext().getAttribute(n);

s.append("<form>")
.append(" nome: <input name=\"nome\" value=\"" + n + "\">")
.append(" valor: <input name=\"valor\" value=\"" + v + "\">")
.append(" Remover? <input name=\"acao\" value=\"remover\" type=\"checkbox\">")
.append(" <input value=\"Alterar\" type=\"submit\">")
.append("</form>");
}

out.print(s);
}
}

Depois de registrado o servlet e o listener no web.xml é só testar. Se por exemplo adicionarmos um atributo de nome pais e valor igual a Brasil, depois alterarmos esse mesmo atributo para Guatemala e ao final removê-lo. Teremos as seguintes linhas escritas no output:

Atributo adicionado: nome=pais, valor=Brasil
Atributo alterado: nome=pais, valorAntigo=Brasil
Atributo removido: nome=pais, valor=Guatemala

Pra que serve e como funciona o ServletContextListener?

A interface ServletContextListener é um listener utilizado para verificar quando uma aplicação é iniciada e finalizada. A classe que implementa essa interface deve sobrescrever os métodos contextInitialized e contextDestroyed que serão chamado assim que a aplicação é iniciada e finalizada respectivamente.

Veja um exemplo de classe implementando este listener:
package meupacote.listeners;

import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;

public class MeuServletContextListener implements ServletContextListener
{
public void contextInitialized(ServletContextEvent e)
{
System.out.println("MeuServletContextListener - iniciado: " +
e.getServletContext().getServerInfo()
);
}

public void contextDestroyed(ServletContextEvent e)
{
System.out.println("MeuServletContextListener - finalizado");
}
}

Assim que registrármos esse listener no web.xml da aplicação, sempre que o contexto for recriado o método contextInitialized será chamado, escrevendo no output "MeuServletContextListener - iniciado: " seguido das informações do container utilizado. Como eu utilizo o tomcat 5, é apresentada a seguinte: "Apache Tomcat/5.5.17". Já o método contextDestroyed será chamado sempre que a aplicação for finalizada.

terça-feira, 10 de abril de 2007

Verificando o funcionamento de SingleThreadModel

SingleThreadModel é do mau. Não é recomendável que se use de maneira alguma, é até uma interface marcada como deprecated. Mas mesmo assim, por algum motivo obscuro ela cai na prova do SCWCD. Para verificar o seu funcionamento criei dois servlets, um sem implementar a famigerada interface e o outro implementando.

Servlet não implementando SingleThreadModel:
public class TesteSemSingleThreadModel extends HttpServlet
{
public static int instancias = 0;

public void init()
{
instancias++;
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
response.getWriter().print("Instancias: " + instancias);
}
}

Servlet implementando SingleThreadModel:
public class TesteComSingleThreadModel extends HttpServlet implements SingleThreadModel
{
public static int instancias = 0;

public void init()
{
instancias++;
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
response.getWriter().print("Instancias: " + instancias);
}
}

Como resultado pude perceber que o primeiro servlet, que não implementa a interface discutida, quando chamado exibe o texto "Instancias: 1" indicando que o método init() foi chamado apenas uma vez. Já o segundo servlet que implementa a SingleThreadModel, quando chamado exibe o texto "Instancias: 2". Ou seja, duas instancias do servlet foram criadas, uma para cada thread do Tomcat, container que estou utilizando.

segunda-feira, 9 de abril de 2007

No forward QueryString pode ser capturada de maneira diferente

Quando um servlet chama repassa a requisição para outro, utilizando o método forward do RequestDispatcher, este outro servlet pode capturar a QueryString do primeiro tanto utilizando o método getQueryString() de HttpServletRequest como utilizando o attributo "javax.servlet.forward.query_string" da requisição.

Segue o servlet que repassa a requisição para o próximo
public class MeuServlet1 extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
request.getRequestDispatcher("/MeuServlet2").forward(request,response);
}
}

Agora o servlet que recebe a requisição:
public class MeuServlet2 extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter p = response.getWriter();
p.print("Query String1: " + request.getQueryString() );
p.print("Query String2: " + request.getAttribute("javax.servlet.forward.query_string") );
}
}

Informação inutil para quem está desenvolvendo, mas importante para quem assim como eu estuda para tirar a SCWCD.

quarta-feira, 4 de abril de 2007

IllegalStateException quando utilizado o forward de RequestDispatcher

Quando uma resposta já tiver sido enviada do servidor de aplicações para o cliente, o método forward da interface RequestDispatcher não pode ser utilizado, pois ele não funciona e gera um IllegalStateException.

Criei um código para testar em quais momentos a resposta é passada ao usuário para descobrir quando é que poderíamos obter essa exceção. Veja:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
String param = request.getParameter("tipo");
if( param==null || param.length() == 0 )
{
response.getWriter().print("Informe o parametro tipo com os valores: ");
response.getWriter().print(" print, flush, stream, close, header ou cookie");
}
else
{
if( "print".equals(param) )
{
response.getWriter().print("alguma coisa");
}
else if( "flush".equals(param) )
{
response.getWriter().print("alguma coisa");
response.getWriter().flush();
}
else if( "stream".equals(param) )
{
ServletOutputStream output = response.getOutputStream();
}
else if( "close".equals(param) )
{
response.getWriter().print("alguma coisa");
response.getWriter().close();
}
else if( "header".equals(param) )
{
response.addHeader("Pragma","no-cache");
}
else if( "cookie".equals(param) )
{
Cookie c = new Cookie("nome","valor");
response.addCookie(c);
}

try
{
request.getRequestDispatcher("/meu.jsp").forward(request,response);
}
catch(IllegalStateException e)
{
e.printStackTrace();
}
}
}
Com isso descobri que a exceção é gerada quando os métodos flush e close do PrintWriter, que é criado pelo HttpServletResponse, são chamados, e quando o método getOutputStream do HttpServletResponse é invocado. Isso é claro, se após chamá-los for executado um forward.

Verifiquei também que se o forward for executado antes, somente a chamada a getOutputStream gera a exception. No tomcat ele dá a seguinte mensagem: getWriter() has already been called for this response.

Método forward de RequestDispatcher prevê algumas exceções

Sempre que utilizar o método forward da interface RequestDispacther é preciso tratar exceções ServletException e IOException. Utilizando uma IDE como o eclipse a gente nunca se preocupa com isso, mas para quem está estudando pro SCWCD, isso é importantíssimo.

Primeiro é preciso importar as classes:
import javax.servlet.ServletException;
import java.io.IOException;
Depois tratar ou capturando a exceção:

try
{
request.getRequestDispatcher("/pagina.jsp").forward(request,response);
}
catch(IOException e)
{
log(e.getMessage());
}

catch(ServletException e)
{
log(e.getMessage());
}
Ou reenviando ela:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
request.getRequestDispatcher("/pagina.jsp").forward(request,response);
}