Teoricamente, recuperar a saída de um Servlet (em geral um documento em texto como o HTML, mas pode ser qualquer arquivo incluso na resposta do servidor) mesmo que seja um JSP, deveria ser fácil. O JSP é convertido em uma classe Java que segue uma interface da especificação do Java Servlet com todas os métodos necessários para recuperar sua saída. Infelizmente, as classes geradas encontram-se em um contexto que o Classloader que executa os servlets não tem acesso. Então, para acessar as saídas dos JSPs é necessário uma abordagem mais indiretas. Na arquitetura J2EE, temos os seguintes recursos à nossa disposição:
- Filtros: antes do envio da resposta ao cliente a cadeia de filtros intercepta a mensagem e pode enviar uma nova mensagem baseado na mensagem interceptada;
- RequestDispatcher.include: esse (novo) método permite um servlet programaticamente adicionar o conteúdo de outro, o que é semelhante a tag jsp:include;
- new URL(urlLocation).openStream: método que abre uma conexão com a localização urlLocation e retorna um stream de dados para leitura;
O uso de filtros ou do include method exige o uso de classes que sigam assinatura HttpServletResponse e do ServletOutputStream e que ao mesmo tempo, guarde as informações das chamadas em algum tipo de repositório. Essa é uma interessante aplicação do Adapter Pattern. O Adapter Pattern pode ser sumarizado da seguinte maneira.
Problema: (Múltipla herança) É desejado o comportamento de um objeto que infelizmente não segue uma assinatura específica.
Solução: Criar uma classe que implemente a interface e direcione as chamadas de forma apropriada ao objeto cujo comportamento é desejado.
Sendo o repositório escolhido para informações, um vetor de bytes. Escolhemos o ByteArrayOutputStream, que pode ser visto como um OutputStream e cujo resultado é guardado no vetor de bytes. Isso nos permite criar um objeto adaptado a interface ServletOutputStream e cuja implementação fica a cargo do ByteArrayOutputStream. Facilita também, utlizar o PrintWriter que já realiza as operações de Writer do ServletOutputStream. Esse PrintWriter deverá escrever diretamente no objeto ByteArrayOutputStream. Isso é feito ao passarmos esse objeto no construtor do PrintWriter.
Uma vez que temos essa classe, implementar a classe ServletResponse é muito simples:
public class FakeServletResponse extends HttpServletResponseWrapper{ private ByteArrayOutputStream contentBuffer; private PrintWriter writer; private FakeServletOutputStream servletOutputStream; public FakeServletResponse(HttpServletResponse response) { super(response); } public PrintWriter getWriter() { if(writer == null){ contentBuffer = new ByteArrayOutputStream(); writer = new PrintWriter(contentBuffer); servletOutputStream = new FakeServletOutputStream(writer, contentBuffer); } return writer; } public ServletOutputStream getOutputStream() throws IOException { getWriter(); return servletOutputStream; } public byte[] getData() { getWriter().flush(); return contentBuffer.toByteArray(); } }E então podemos utilizar o include method:
private FakeServletResponse includeJsp(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute("geraNota", Boolean.TRUE); FakeServletResponse fakeServletResponse = new FakeServletResponse(response); BuscaNotaAction buscaNotaAction = new BuscaNotaAction(); buscaNotaAction.execute(mapping, form, request, fakeServletResponse); RequestDispatcher requestDispatcher = request.getRequestDispatcher("/jsp/operacoes/notaFiscal/imprimeNota.jsp"); requestDispatcher.include(request, fakeServletResponse); return fakeServletResponse; }
Comentários