Relatórios BIRT com Boot Spring
1. Introdução
Neste tutorial, vamos integrar BIRT (Business Intelligence and Reporting Tools) com Spring Boot MVC, para servir relatórios estáticos e dinâmicos em formato HTML e PDF.
2. O que é o BIRT
BIRT is an open source engine to create data visualizations que podem ser integrados em aplicativos da web Java.
É um projeto de software de nível superior dentro da Eclipse Foundation e alavanca as contribuições da IBM e Innovent Solutions. Foi iniciado e patrocinado pela Actuate no final de 2004.
A estrutura permite criar relatórios integrados a uma ampla variedade de fontes de dados.
3. Dependências do Maven
BIRT has two main components: a visual report designer to create report design files, and a runtime component para interpretar e renderizar esses designs.
Em nosso aplicativo da web de exemplo, vamos usar ambos no topo do Spring Boot.
3.1. Dependências da estrutura do BIRT
Como estamos acostumados a pensar em termos de gerenciamento de dependência, a primeira escolha seria procurar o BIRT no Maven Central.
No entanto,the latest official version of the core library available is 4.6 from 2016, enquanto emEclipse download page, podemos encontrar links para pelo menos duas versões mais recentes (the current is 4.8).
Se escolhermos a versão oficial, a maneira mais fácil de ter o código instalado e funcionando é baixar o pacoteBIRT Report Engine, que é um aplicativo da web completo também útil para o aprendizado. Em seguida, precisamos copiar sua pastalib em nosso projeto (cerca de 68 MB de tamanho) e dizer ao IDE para incluir todos os jars nele.
Nem é preciso dizer que, usando essa abordagem,we’ll be able to compile only through the IDE, já que o Maven não encontrará esses jars a menos que os configuremos e os instalemos manualmente (mais de 100 arquivos!) Em nosso repositório local.
Felizmente,Innovent Solutions has decided to take matters in its hands and published on Maven Central its own builds das dependências BIRT mais recentes, o que é ótimo, pois gerencia para nós todas as dependências necessárias.
Lendo comentários em fóruns online, não está claro se esses artefatos estão prontos para produção, mas a Innovent Solutions trabalhou no projeto ao lado da equipe do Eclipse desde o início, então nosso projeto depende deles.
Incluir o BIRT agora é muito fácil:
com.innoventsolutions.birt.runtime
org.eclipse.birt.runtime_4.8.0-20180626
4.8.0
3.2. Dependências de inicialização do Spring
Agora que o BIRT é importado para o nosso projeto, precisamos apenas adicionar as dependências padrão do Spring Boot em nosso arquivo pom.
Há uma armadilha, no entanto, porque o jar BIRT contém sua própria implementação deSlf4J, que não joga bem comLogback e lança uma exceção de conflito durante a inicialização.
Como não podemos retirá-lo do jarro, para resolver este problema,we need to exclude Logback:
org.springframework.boot
spring-boot-starter-logging
ch.qos.logback
logback-classic
Agora estamos finalmente prontos para começar!
4. Relatórios BIRT
Na estrutura BIRT,a report is a long XML configuration file, identificado pela extensãorptdesign.
It tells the Engine what to draw and where, desde o estilo de um título até as propriedades necessárias para conectar a uma fonte de dados.
Para um relatório dinâmico básico, precisamos configurar três coisas:
-
a fonte de dados (em nosso exemplo, usamos um arquivo CSV local, mas pode ser facilmente uma tabela de banco de dados)
-
os elementos que queremos exibir (gráficos, tabelas etc.)
-
o design da página
O relatório está estruturado como uma página HTML, com cabeçalho, corpo, rodapé, scripts e estilos.
The framework provides an extensive set of components to choose from out-of-the-box, incluindo integração com fontes de dados, layouts, gráficos e tabelas principais. E podemos estendê-lo para adicionar o nosso!
Existem duas maneiras de gerar um arquivo de relatório: visual ou programático.
5. O Designer de Relatório Eclipse
Para facilitar a criação de relatórios, o pluginthe Eclipse team built a report design tool para seu IDE popular.
This tool features an easy drag & drop interface dePalette à esquerda, que abre automaticamente a janela de configuração do novo componente que adicionamos à página. Também podemos ver todas as customizações disponíveis para cada componente clicando sobre ele na página e depois no botãoProperty Editor (destacado na imagem abaixo).
Para visualizar toda a estrutura da página em árvore, basta clicar no botãoOutline.
A guiaData Explorer também contém as fontes de dados definidas para nosso relatório:
O relatório de amostra exibido na imagem pode ser encontrado no caminho<project_root>/reports/csv_data_report.rptdesign
Outra vantagem de optar pelo designer visual é a documentação on-line, que se concentra mais nessa ferramenta em vez da abordagem programática.
If we’re already using Eclipse, we just need to install the BIRT Report Design plugin, que inclui uma perspectiva predefinida e o editor visual.
For those developers who are not currently using Eclipsee não deseja alternar,there’s an Eclipse Report Designer package, que consiste em uma instalação portátil do Eclipse com o plug-in BIRT pré-instalado.
Depois que o arquivo de relatório estiver finalizado, podemos salvá-lo em nosso projeto e voltar à codificação em nosso ambiente preferido.
6. A abordagem programática
Também podemosdesign a report using only code, mas essa abordagem é muito mais difícil devido à documentação pobre disponível, portanto, esteja preparado para cavar no código-fonte e fóruns online.
Também vale a pena considerar que todos ostedious design details como tamanho, comprimento e posição da gradeare a lot easier to deal with using the designer.
Para provar este ponto, aqui está um exemplo de como definir uma página estática simples com uma imagem e um texto:
DesignElementHandle element = factory.newSimpleMasterPage("Page Master");
design.getMasterPages().add(element);
GridHandle grid = factory.newGridItem(null, 2, 1);
design.getBody().add(grid);
grid.setWidth("100%");
RowHandle row0 = (RowHandle) grid.getRows().get(0);
ImageHandle image = factory.newImage(null);
CellHandle cell = (CellHandle) row0.getCells().get(0);
cell.getContent().add(image);
image.setURL("\"https://www.example.com/wp-content/themes/example/favicon/favicon-96x96.png\"");
LabelHandle label = factory.newLabel(null);
cell = (CellHandle) row0.getCells().get(1);
cell.getContent().add(label);
label.setText("Hello, example world!");
Este código irá gerar um relatório simples (e feio):
O relatório de amostra exibido na imagem acima pode ser encontrado neste caminho:<project_root>/reports/static_report.rptdesign.
Depois de codificar a aparência do relatório e quais dados ele deve exibir, podemos gerar o arquivo XML executando nossa classeReportDesignApplication.
7. Anexando uma fonte de dados
Mencionamos anteriormente que o BIRT suporta muitas fontes de dados diferentes.
Para o nosso projeto de exemplo, usamos um arquivo CSV simples com três entradas. Ele pode ser encontrado na pastareports e consiste em três linhas simples de dados, além de cabeçalhos:
Student, Math, Geography, History
Bill, 10,3,8
Tom, 5,6,5
Anne, 7, 4,9
7.1. Configurando a fonte de dados
Para permitir que o BIRT use nosso arquivo (ou qualquer outro tipo de fonte),we have to configure a Data Source.
Para nosso arquivo, criamos umFlat File Data Source com o designer de relatórios, tudo em apenas algumas etapas:
-
Abra a perspectiva do designer e observeoutline à direita.
-
Clique com o botão direito no íconeData Sources.
-
Selecione o tipo de fonte desejado (no nosso caso, a fonte de arquivo simples).
-
Agora podemos optar por carregar uma pasta inteira ou apenas um arquivo. Usamos a segunda opção (se nosso arquivo de dados estiver no formato CSV, queremos ter certeza de usar a primeira linha como indicador do nome da coluna).
-
Teste a conexão para garantir que o caminho esteja correto.
Anexamos algumas fotos para mostrar cada etapa:
7.2. O conjunto de dados
A fonte de dados está pronta, mas ainda precisamos definir nossoData Set, que são os dados reais mostrados em nosso relatório:
-
Abra a perspectiva do designer e observeoutline à direita.
-
Clique com o botão direito no íconeData Sets.
-
Selecione oData Sourcee desejado e o tipo (em nosso caso, há apenas um tipo).
-
A próxima tela depende do tipo de fonte de dados e conjunto de dados que estamos selecionados: em nosso caso, vemos uma página onde podemos selecionar as colunas a serem incluídas.
-
Após a conclusão da instalação, podemos abrir a configuração a qualquer momento clicando duas vezes em nosso conjunto de dados.
-
EmOutput Columns, podemos definir o tipo correto de dados exibidos.
-
Podemos ver uma prévia clicando emPreview Results.
Novamente, algumas fotos para esclarecer essas etapas:
7.3. Outros tipos de fonte de dados
Conforme mencionado na etapa 4 da configuração deData Set, as opções disponíveis podem mudar dependendo doData Source referido.
Para o nosso arquivo CSV, o BIRT fornece opções relacionadas a quais colunas mostrar, o tipo de dados e se queremos carregar o arquivo inteiro. Por outro lado, se tivéssemos uma fonte de dados JDBC, talvez tenhamos que escrever uma consulta SQL ou um procedimento armazenado.
No menuData Sets,we can also join two or more data sets in a new data set.
8. Renderizando o relatório
Quando o arquivo de relatório estiver pronto, temos que passá-lo ao mecanismo para renderização. Para fazer isso, há algumas coisas a serem implementadas.
8.1. Inicializando o mecanismo
A classeReportEngine, que interpreta os arquivos de design e gera o resultado final, faz parte da biblioteca de tempo de execução do BIRT.
Ele usa um monte de ajudantes e tarefas para fazer o trabalho, tornando-o bastante intensivo em recursos:
Fonte da imagem: documentação do Eclipse BIRT
There is a significant cost associated with creating an engine instance, principalmente devido ao custo de carregamento de extensões. Portanto,we should create just one ReportEngine instance and use it to run multiple reports.
O mecanismo de relatório é criado por meio de uma fábrica fornecida porPlatform. Antes de criar o mecanismo, temos que iniciar oPlatform, que carregará os plug-ins apropriados:
@PostConstruct
protected void initialize() throws BirtException {
EngineConfig config = new EngineConfig();
config.getAppContext().put("spring", this.context);
Platform.startup(config);
IReportEngineFactory factory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
birtEngine = factory.createReportEngine(config);
imageFolder = System.getProperty("user.dir") + File.separatorChar + reportsPath + imagesPath;
loadReports();
}
Quando não precisarmos mais dele, podemos destruí-lo:
@Override
public void destroy() {
birtEngine.destroy();
Platform.shutdown();
}
8.2. Implementando o formato de saída
BIRT already supports multiple output formats:HTML, PDF, PPT, and ODT, para citar alguns.
Para o projeto de amostra, implementamos dois deles com os métodosgeneratePDFReportegenerateHTMLReport.
Eles diferem um pouco, dependendo das propriedades específicas necessárias, como formato de saída e manipuladores de imagem.
De fato, os PDFs incorporam imagens ao texto, enquanto os relatórios HTML precisam gerá-los e / ou vinculá-los.
Assim,the PDF rendering function is quite straightforward:
private void generatePDFReport(IReportRunnable report, HttpServletResponse response,
HttpServletRequest request) {
IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report);
response.setContentType(birtEngine.getMIMEType("pdf"));
IRenderOption options = new RenderOption();
PDFRenderOption pdfRenderOption = new PDFRenderOption(options);
pdfRenderOption.setOutputFormat("pdf");
runAndRenderTask.setRenderOption(pdfRenderOption);
runAndRenderTask.getAppContext().put(EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT, request);
try {
pdfRenderOption.setOutputStream(response.getOutputStream());
runAndRenderTask.run();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
runAndRenderTask.close();
}
}
Embora a função de renderização de HTML precise de mais configurações:
private void generateHTMLReport(IReportRunnable report, HttpServletResponse response,
HttpServletRequest request) {
IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report);
response.setContentType(birtEngine.getMIMEType("html"));
IRenderOption options = new RenderOption();
HTMLRenderOption htmlOptions = new HTMLRenderOption(options);
htmlOptions.setOutputFormat("html");
htmlOptions.setBaseImageURL("/" + reportsPath + imagesPath);
htmlOptions.setImageDirectory(imageFolder);
htmlOptions.setImageHandler(htmlImageHandler);
runAndRenderTask.setRenderOption(htmlOptions);
runAndRenderTask.getAppContext().put(
EngineConstants.APPCONTEXT_BIRT_VIEWER_HTTPSERVET_REQUEST, request);
try {
htmlOptions.setOutputStream(response.getOutputStream());
runAndRenderTask.run();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
runAndRenderTask.close();
}
}
Mais digno de nota,we set the HTMLServerImageHandler em vez de deixar o manipulador padrão. Esta pequena diferença tem um grande impacto na tagimg gerada:
-
the default handler links the img tag to the file system path, bloqueado para segurança por muitos navegadores
-
os linksHTMLServerImageHandler para a URL do servidor
Com o métodosetImageDirectory, especificamos onde o mecanismo salvará o arquivo de imagem gerado.
Por padrão, o manipulador gera um novo arquivo a cada solicitação, portantowe could add a caching layer or a deletion policy.
8.3. Publicando as imagens
No caso do relatório HTML, os arquivos de imagem são externos e, portanto, precisam estar acessíveis no caminho do servidor.
No código acima, com o métodosetBaseImageURL, informamos ao mecanismo qual caminho relativo deve ser usado no link da tagimg, portanto, precisamos ter certeza de que o caminho está realmente acessível!
Por esse motivo, em nossoReportEngineApplication, configuramos o Spring para publicar a pastaimages:
@SpringBootApplication
@EnableWebMvc
public class ReportEngineApplication implements WebMvcConfigurer {
@Value("${reports.relative.path}")
private String reportsPath;
@Value("${images.relative.path}")
private String imagesPath;
...
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler(reportsPath + imagesPath + "/**")
.addResourceLocations("file:///" + System.getProperty("user.dir") + "/"
+ reportsPath + imagesPath);
}
}
Seja qual for o caminho que escolhermos, temos que nos certificar de que o mesmo caminho seja usado aqui e nohtmlOptions do snippet anterior, ou nosso relatório não será capaz de exibir imagens.
9. Exibindo o relatório
O último componente necessário para preparar nosso aplicativo é umController para retornar o resultado renderizado:
@RequestMapping(method = RequestMethod.GET, value = "/report/{name}")
@ResponseBody
public void generateFullReport(HttpServletResponse response, HttpServletRequest request,
@PathVariable("name") String name, @RequestParam("output") String output)
throws EngineException, IOException {
OutputType format = OutputType.from(output);
reportService.generateMainReport(name, format, response, request);
}
Com o parâmetrooutput, podemos deixar o usuário escolher o formato desejado - HTML ou PDF.
10. Testando o relatório
Podemos iniciar o aplicativo executando a classeReportEngineApplication.
Durante a inicialização, a classeBirtReportService carregará todos os relatórios encontrados na pasta<project_root>/reports.
Para ver nossos relatórios em ação, basta apontar nosso navegador para:
-
/report/csv_data_report?output=pdf
-
/report/csv_data_report?output=html
-
/report/static_report?output=pdf
-
/report/static_report?output=html
Esta é a aparência do relatóriocsv_data_report:
Para recarregar um relatório após alterar o arquivo de design, apenas apontamos nosso navegador para/report/reload.
11. Conclusão
Neste artigo, integramos o BIRT ao Spring Boot, explorando as armadilhas e desafios, mas também seu poder e flexibilidade.
O código-fonte do artigo está disponívelover on GitHub.