Create Great Reports with JasperReports

Share this article

JasperReports_Logo

Ruby on Rails is an amazing framework for building web applications. The Ruby and Rails ecosystems are very active and mature, with many libraries for solving the various problems encountered by developers. But one particular area where I feel Ruby lacks options is reporting. Many solutions exist in Rails for generating reports, most of them rely on generating HTML and converting to PDF. This approach has its advantages, but falls apart for complex and pixel perfect reports.

Enter JasperReports. JasperReports is a very popular open source reporting library widely used in Java world. Many of you may be curious about how to use a Java library in Rails. There are many approaches, but the one I prefer is to use JRuby. This requires you to convert your application to JRuby, which is quite simple to do these days. So, let’s get started.

Sample Rails Application

We will create a sample Rails application with a contact model and CRUD actions. The application will use JRuby, Rails 4.2, and SQLite as database to keep things simple. First of all, install JRuby with RVM or rbenv.

Switch to JRuby and bundle install the latest Rails gem. Now create a new Rails application, like so:

rails new contactbook

After the application is generated, create a Contact scaffold, which has amodel and CRUD resource operations:

cd contactbook
rails g scaffold Contact name:string address:string city:string phone:string email:string

Now migrate the database:

rake db:migrate

Let’s check how its working:

rails s

Point your favorite browser to http://localhost:3000/contacts and make sure everything is working properly. Create some records so we can use those later.

A Sample JasperReport

Let’s create a simple report that shows list of contacts. Download the JasperSoft Studio suitable to your platform from the Jaspersoft Studio site. Install and run Jaspersoft Studio. It is full fledged IDE based on Eclipse, specifically to design JasperReports. It has a learning curve, for sure, but we will not get into those details for now. You can refer to Jaspersoft Studio – Resources for more information.

Create a report named contacts using Jaspersoft Studio which should look as follows:

jaspersoft-studio

Compile the report and paste the contacts.jasper file into app/reports/ in our Rails application.

Integrating JasperReports

Now comes the important part, integrating JasperReports into the Rails application. We will be using JasperReports 6.1 version for this tutorial. Head over to the JasperReports Library page and download jasperreports-6.1.0-project.tar.gz. This archive contains the JasperReports JAR files in its dist folder, along with dependencies in lib folder. Copy all the files from both of those folders and put them in a folder named jasperreports under the lib directory in our Rails application.

contactbook
  - app
  ...
  - lib
    - jasperreports
      - jasperreports-6.1.0.jar
      ...

We have all required files for JasperReports in place, so let’s create a class to integrate JasperReports. Create a file called jasper_report.rb in lib directory with following code:

Dir.entries("#{Rails.root}/lib/jasperreports").each do |lib|
  require "jasperreports/#{lib}" if lib =~ /\.jar$/
end

require 'java'

java_import Java::net::sf::jasperreports::engine::JasperFillManager
java_import Java::net::sf::jasperreports::engine::JasperExportManager
java_import Java::net.sf.jasperreports.engine.JRResultSetDataSource

class JasperReport
  DIR = "#{Rails.root}/app/reports"

  def initialize(report, query, params = nil)
    @model = report
    @report_params = params
    @conn = ActiveRecord::Base.connection.jdbc_connection
    @query = query
  end

  def to_pdf
    stmt = @conn.create_statement
    @result = JRResultSetDataSource.new(stmt.execute_query(@query))
    report_source = "#{DIR}/#{@model}.jasper"
    raise ArgumentError, "#@model does not exist." unless File.exist?(report_source)
    params = {}
    params.merge!(@report_params) if @report_params.present?
    fill = JasperFillManager.fill_report(report_source, params, @result)
    pdf = JasperExportManager.export_report_to_pdf(fill)
    return String.from_java_bytes(pdf)
  end
end

Let’s see what the code actually does. First we require all JasperReports files into our class, like so:

Dir.entries("#{Rails.root}/lib/jasperreports").each do |lib|
  require "jasperreports/#{lib}" if lib =~ /\.jar$/
end

Add the Java import declarations for JasperReports:

require 'java'

java_import Java::net::sf::jasperreports::engine::JasperFillManager
java_import Java::net::sf::jasperreports::engine::JasperExportManager
java_import Java::net.sf.jasperreports.engine.JRResultSetDataSource

Define the location where all reports will be stored:

DIR = "#{Rails.root}/app/reports"

Add the initialization code in the class constructor:

def initialize(report, query, params = nil)
    @model = report
    @report_params = params
    @conn = ActiveRecord::Base.connection.jdbc_connection
    @query = query
end

As a first step, we initialized all required variables:

  • The report’s file name – report
  • The report’s SQL query – query
  • Any optional parameters to pass on to the report – params

@conn is a JDBC connection from the ActiveRecord connection pool, since JasperReports requires a JDBC connection to execute the query.

Finally, we have added a method to fill and export the report to PDF format:

def to_pdf
  stmt = @conn.create_statement
  @result = JRResultSetDataSource.new(stmt.execute_query(@query))
  report_source = "#{DIR}/#{@model}.jasper"
  raise ArgumentError, "#@model does not exist." unless File.exist?(report_source)
  params = {}
  params.merge!(@report_params) if @report_params.present?
  fill = JasperFillManager.fill_report(report_source, params, @result)
  pdf = JasperExportManager.export_report_to_pdf(fill)
  return String.from_java_bytes(pdf)
end

The first item is a JDBC statement, stmt = @conn.create_statement, which returns a JDBC ResultSet by executing @result = JRResultSetDataSource.new(stmt.execute_query(@query)). The report (.jasper) file created earlier using JasperStudio is set to the report_source. After that, invoke the JasperFillManager.fill_report to fill the report with the provided ResultSet and parameters. Lastly, invoke JasperExportManager.export_report_to_pdf to export the report into a PDF bytestream which is returned using String.from_java_bytes.

We now have the report as a PDF bytestream, but we’ve no way to send it to user, yet. So, add a small helper method in application_controller.rb as follows:

def respond_to_report(name, query, filename, download = false, report_params = nil)
  @report = JasperReport.new(name, query, report_params) 
  disposition = (download.nil? || download == false) ? 'inline' : 'attachment'
  send_data @report.to_pdf, :filename => filename, :type => :pdf, :disposition => disposition
end

This helper method simplifies the report invocation and sends the response back to the user. There are also added options for providing a filename, along with a disposition option to open the file within the browser or download it as an attachment.

Now, we will to add a action to the ContactsController that calls the report. Add the following code to app/controllers/contacts_controller.rb:

def report
  respond_to_report('contacts', 'select * from contacts', 'contacts.pdf')
end

As we have no parameters to pass and want to open the report in browser, we have skipped the download and report_params from the method call.

Update routes.rb to add the new action:

resources :contacts do
  get :report, on: :collection
end

Add a link for the report in the contacts/index.html.erb view:

...
<h1>Listing Contacts</h1>

<%= link_to 'Download as PDF', report_contacts_path %>
...

Fire up the server and go to http://localhost:3000/contacts. Click on ‘Download as PDF’. You should now see a PDF file with all contacts listed.

final-report

Wrapping Up

This was a quick primer on how to integrate JasperReports with Rails to create reports without much effort. There is much more that can be done with JasperReports and Rails, such as search criteria, complex reports, subreports, etc. But that’s for another tutorial.

I hope you enjoyed this post. Your comments and insights are always welcome.

Frequently Asked Questions (FAQs) about JasperReports

What is the difference between JasperReports Library and JasperReports Server?

JasperReports Library is a reporting engine that can be embedded into any Java application. It provides features like exporting reports to various formats (PDF, XLS, etc.), and it can pull data from various data sources. On the other hand, JasperReports Server is a standalone server application built on JasperReports Library. It provides a user interface for managing and running reports, and it also includes features for scheduling, distribution, and security.

How can I integrate JasperReports into my Java application?

To integrate JasperReports into your Java application, you need to add the JasperReports library to your project. You can do this by downloading the library and adding it to your project’s classpath, or by using a dependency management tool like Maven or Gradle. Once the library is added, you can use its API to create, compile, and run reports.

Can I use JasperReports with non-Java languages?

While JasperReports is primarily designed for Java, it can be used with other languages that run on the JVM, such as Groovy or Kotlin. However, using JasperReports with non-JVM languages can be more challenging, as it requires a bridge between the language and the JVM.

How can I create a report template in JasperReports?

Report templates in JasperReports are created using a language called JRXML, which is an XML-based language. You can create a JRXML file using any text editor, but there are also specialized tools like Jaspersoft Studio that provide a graphical interface for creating report templates.

What data sources can JasperReports use?

JasperReports can use a wide variety of data sources, including SQL databases, XML files, CSV files, and Java objects. It can also use custom data sources, which you can create by implementing the JRDataSource interface.

How can I export reports to different formats in JasperReports?

JasperReports provides a number of exporter classes that can be used to export reports to different formats. For example, you can use the JRPdfExporter class to export a report to PDF, or the JRXlsExporter class to export a report to Excel.

Can I use JasperReports for web reporting?

Yes, JasperReports can be used for web reporting. You can use the JasperReports Server for this purpose, which provides a web interface for managing and running reports. Alternatively, you can embed the JasperReports library into your own web application.

How can I add charts and graphs to my reports in JasperReports?

JasperReports supports a variety of chart types, including bar charts, pie charts, line charts, and more. You can add a chart to your report by including a chart element in your JRXML template.

Can I use JasperReports for big data reporting?

Yes, JasperReports can be used for big data reporting. It supports various big data technologies like Hadoop and NoSQL databases. However, keep in mind that reporting on big data can be challenging due to the large volume of data and the need for specialized processing techniques.

Is there a community edition of JasperReports?

Yes, there is a community edition of JasperReports that is free to use. It includes the core features of JasperReports, but it lacks some of the advanced features found in the commercial editions.

Devdatta KaneDevdatta Kane
View Author

Devdatta Kane is a software developer and designer based in Pune, India. He works with Radinik Technologies building traceability solutions for a variety of industries. He is also the lead developer of refers2, a CRM for small businesses. He works in Ruby on Rails, but likes to dabble with various new technologies as well. An aspiring photographer and passionate traveler, he loves traveling on his motorcycle, capturing experiences through camera.

GlennGJRuby
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week