Artigos
Java
Resolvendo problemas de integração entre Struts 2.1.6 e JasperReports
Nesta última semana de trabalho, deleguei uma atividade a um dos desenvolvedores da minha equipe: integrar Struts 2.1.6 e JasperReports, desenvolvendo um relatório simples e, posteriormente, desenvolvendo um relatório com subrelatório.
Após pesquisa inicial, o desenvolvedor resolveu seguir um excelente exemplo disponibilizado por Bruce Phillips, na URL:
Após baixar o código fonte da aplicação exemplo, o desenvolver realizou o deployment desta no TomCat e executou a pequena aplicação web, com o intuito de verificar se a solução estava funcional. E ela realmente estava, o cenário parecia muito favorável. Entretanto havia uma diferença: a aplicação fora distribuída utilizando como base a realease 2.0.11 do framework Struts 2, ao passo que o software que estamos desenvolvendo está sob a release 2.1.6 do framework Struts 2. Ao migrar a mesma solução do exemplo para a release mais nova, utilizada pelo software que estamos desenvolvendo, os problemas começaram a aparecer.
O relatório disponibilizado pela aplicação exemplo (neste momento, rodando sob as bibliotecas da release 2.1.6 do Struts 2) exibia um arquivo em formato PDF completamente em branco, sem mostrar sequer a formatação dada ao relatório no iReport. Ao analisar-se o log do TomCat, verificou-se a seguinte mensagem:
WARNING: Data source value for data source beanCollection was null.
Vamos analisar a situação:
1) Na aplicação exemplo, o autor define o seguinte mapeamento de Action:
Na versão 2.0.11 do Struts 2, o mapeamento acima significa:
1.1 - Sempre que a URL http:/servidor/aplicacao/myJasperTest.action for solicitada, o método public String execute() da Action "action.JasperAction" será invocado;
1.2. - O fluxo de execução retornará à mesma "action.JasperAction" e executurá, desta vez, o método public static ArrayList<OBJETOPERTINENTE><PERSON> getBeanCollection(), que deverá retornar um ArrayList dos objetos que serão listados no relatório /jasper/contacts.jasper;
1.3 - O fluxo de execução passará o ArrayList obtido à engine Jasper, que processará o relatório e exibirá o PDF no browser, contendo todos os dados populados no ArrayList.
Porém, na versão 2.1.6 do Struts 2, o mesmo mapeamento tem um significado diferente:
1.1 - Sempre que a URL http:/servidor/aplicacao/myJasperTest.action for solicitada, o método public String doReport() da Action "action.JasperAction" será invocado;
1.2 - A Action "action.JasperAction" deverá possuir um atributo ArrayList<OBJETOPERTINENTE> chamado beanCollection (repare que é o mesmo nome informado na tag param name="dataSource", no mapeamento acima) , bem como deve possuir os acessores do atributo;
1.3 - O método public String doReport() deverá popular o ArrayList. O fluxo de execução passará o ArrayList à engine Jasper, que processará o relatório e exibirá o PDF no browser, contendo todos os dados populados no ArrayList.
2) Considerando as diferenças expostas acima, para atendermos à release 2.1.6. do Struts, teremos, então, a seguinte implementação de JasperAction:
package
action;
import java.util.ArrayList;
import
model.Person;
import
model.Phone;
import
com.opensymphony.xwork2.ActionSupport;
public
class JasperAction extends ActionSupport {
private static final long serialVersionUID = 1L;
// O atributo beanCollection, conforme definido em param name="dataSource".
private ArrayList<PERSON> beanCollection = new ArrayList<PERSON>();
public String execute() throws Exception {
return SUCCESS;
}
// O método a ser invocado sempre que um result de uma Action for do tipo "jasper".
public String doReport() throws Exception {
setBeanCollection(createBeanCollection());
return SUCCESS;
}
// Acessores de beanCollection
public ArrayList<PERSON> getBeanCollection() {
return beanCollection;
}
public void setBeanCollection(ArrayList<PERSON> beanCollection) {
this.beanCollection = beanCollection;
}
// Um método definido para popular o ArrayList, antes do método do Report repassá-lo à engine Jasper.
public ArrayList<PERSON> createBeanCollection() {
ArrayList<PERSON> people = new ArrayList<PERSON>();
ArrayList<PHONE> phones = new ArrayList<PHONE>();
// Create Phones for Person1
Phone phone1 = new Phone("1","913-906-9000");
Phone phone2 = new Phone("2", "913-906-9001");
Phone phone3 = new Phone("3", "913-906-9002");
phones.add(phone1);
phones.add(phone2);
phones.add(phone3);
//Create Person1
Person person = new Person("Barack", "Obama", phones );
people.add(person);
// Create Phones for Person2
phone1 = new Phone("4","913-907-6000");
phone2 = new Phone("5", "913-907-6001");
phone3 = new Phone("6", "913-907-6002");
phones = new ArrayList<PHONE≶();
phones.add(phone1);
phones.add(phone2);
phones.add(phone3);
//Create Person2
person = new Person("Bruce", "Lee", phones );
people.add(person);
// Create Phones for Person3
phone1 = new Phone("7","913-908-6000");
phone2 = new Phone("8", "913-908-6001");
phone3 = new Phone("9", "913-908-6002");
phones = new ArrayList<PHONE>();
phones.add(phone1);
phones.add(phone2);
phones.add(phone3);
//Create Person3
person = new Person("Samuel", "L. Jackson", phones );
people.add(person);
return people;
}
}
3) Considerações:
3.1 - Deve-se verificar se todas as dependências do seu projeto estão consistentes em relação às dependências do plugin Struts2-Jasperreports-Plugins, conforme o link a seguir:
http://struts.apache.org/2.x/struts2-plugins/struts2-jasperreports-plugin/dependencies.html
3.2 - A solução acima funcionou utilizando o seguinte conjunto de bibliotecas (jars):
commons-beanutils-1.7.0.jar
commons-collections-3.2.1.jar
commons-digester-1.8.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging-1.0.4.jar
freemarker-2.3.13.jar
itext-1.3.1.jar
jasperreports-3.0.0.jar
jcommon-1.0.0.jar
jdtcore-3.1.0.jar
jfreechart-1.0.1.jar
log4j-1.2.15.jar
ognl-2.6.11.jar
ojdbc14.jar
spring-test-2.5.6.jar
struts2-core-2.1.6.jar
struts2-dojo-plugin.2.1.6.jar
struts2-jasperreports=plugin-2.1.6.jar
struts2-tiles-plugin-2.1.6.jar
tiles-api-2.1.2.jar
tiles-core-2.1.2.jar
tiles-jsp.2.1.2.jar
tiles-servlet-2.1.2.jar
xwork-2.1.2.jar
3) Conclusão: O processo de integração entre a realease 2.1.6 do Struts e o framework para exibição e criação de relatórios, JasperReports, apresenta mudanças estruturais bastante significativas, se compararmos as implementações de Action que atendem à release 2.1.6 com as implementações de Action que atendem às releases anteriores.