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:
img
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.