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