Integrating SWT/RCP and Jasper Reports

This tutorial is form developers who are trying to integrate jasper reports into a swt/rcp application. This article covers the following aspects of jasper reports

1. Creating a report fetching data from jdbc datasource
2. Loading the report into a rcp app.
3. Refreshing the report on fixed intervals.

Most importantly, it covers the messy part of loading the report into a swt/rcp app.

Requirements:-
1. Have the reports ready and compiled into .jasper files.
2. Download SWTJasperViewer from https://sourceforge.net/projects/swtjasperviewer

Basic info:-
Our end result is to load a report into a view in an rcp app. For this, we will use the SWTJasperViewer which takes a JasperPrint object as an input.

Step 1:
Create a jasper report – CustomerReport which has a subreport CustomerProducts. You can do this using a wysiwyg designer like JasperAssistant (can be found at http://www.jasperassistant.com). The output of of JasperAssistant is .jasper files. This is a compiled format.

Step 2:
The .jasper file contains the layout and design information for the report complete with the placeholders and other static objects. Once we have this, the next step is to fetch data from the database and fill the report with data. So, lets first create a db connection.

DBConnector.java

package com.myproject.reports.data;

import java.sql.Connection;
import java.sql.DriverManager;

public class DBConnector {

public static Connection getConnection() {

Connection con = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
con = DriverManager.getConnection("jdbc:oracle:thin:@DBSERVER:1521:DBSID","USERNAME"
,"PASSWORD");
} catch (Exception e) {
e.printStackTrace();
}
return con;


}
}

Now, lets use this connection to generate our report. But, to generate the report, we need to have something else ready. The fix for the NoClassFound error which comes when we try to compile the report from a rcp app. This is because eclipse doesnt allow loading 3rd party classes. When I faced this issue, many suggested to take the “Buddy Loader” path. But, that didnt work as well. To set a thirdparty class as a buddy, we will need to register its jar as a buddy in its manifest. This may not always be possible. And in my case, it wasnt. So, my other option was to tweak Eclipse’ classloader. And so, we make a copy of the ContextFinder class with some minor modifications to the basicFindClassLoader method. In the cloned copy of the ContextFinder, replace the basicFindClassLoader with the one given below.

//Modified basicFindClassLoader
ClassLoader basicFindClassLoader() {
Class[] stack = contextFinder.getClassContext();
ClassLoader result = null;
for (int i = 1; i 
ClassLoader tmp = stack[i].getClassLoader();
//if (stack[i] != ContextFinder.class && tmp != null) {
// result = tmp;
// break;
//}

if (stack[i] != ContextFinder.class && tmp != null
&& checkClassLoader(tmp)) {
return tmp;
}

}
//if (checkClassLoader(result))
// return result;
return null;
}

Now, we will load this classloader into the memory and use it to generating our report. Then, we will replace it with the original classloader.

ReportGen.java

import java.util.HashMap;
import java.util.Map;
import java.sql.*;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;

public class ReportGen {

public static void main(String[] args){

}

public static JasperPrint generateReport(Integer custInfo){
Connection conn = null;
JasperPrint myJPrint = null;

//Taking backup of the default classloader
ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader();

try {
//Loading my custom classloader
Thread.currentThread().setContextClassLoader(new com.zafinlabs.jasper.fix.ContextFinder(Thread.currentThread().getContextClassLoader()));

//Connecting to the database
conn = DBConnector.getConnection();

//Loading my jasper file
JasperReport jasperReport = null;
jasperReport = (JasperReport) net.sf.jasperreports.engine.util.JRLoader.loadObject(ReportGen.class.getClassLoader()
.getResourceAsStream("CustomerReport.jasper"));

//Passing parameters to the report
Map parameters = new HashMap();
parameters.put("p_CustomerId", custInfo);

//Filling the report with data from
//the database based on the parameters passed.
myJPrint = JasperFillManager.fillReport(jasperReport, parameters, conn);

//Closing the database connection
conn.close();

}catch(JRException jrExp){
jrExp.printStackTrace();
} catch (SQLException e) {
try {
conn.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{

//Replacing my custom classloader with the original classloader.
Thread.currentThread().setContextClassLoader(systemClassLoader);
}
return myJPrint;
}
}

The output here is a JasperPrint object which can be loaded into a view. So, now lets move on the the report view which will display the report we just created. We do this by using the SWTJasperViewer. So, remember to add the jar to the classpath.

ReportView.java

package com.myproject.reports.views;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.part.ViewPart;

import com.jasperassistant.designer.viewer.ReportViewer;
import com.jasperassistant.designer.viewer.StatusBar;
import com.jasperassistant.designer.viewer.ViewerComposite;

public class ReportView extends ViewPart {

public static final String ID_REPORT_VIEW = "com.myproject.reports.views.ReportView";

//Using the SWTJasperViewer
private ViewerComposite viewerComposite;

public void createPartControl(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
container.setLayout(layout);

viewerComposite = new ViewerComposite(container,SWT.BORDER);
viewerComposite.setLayoutData(new GridData(GridData.FILL,GridData.FILL,true,true));

}

public void setFocus() {

}

public ReportViewer getReportViewer() {
return (ReportViewer)viewerComposite.getReportViewer();
}

public Composite getReportViewerComposite() {
return viewerComposite;
}
}

Now, we have the view ready. Lets move on to write the action which calls the view.

ReportAction.java

import net.sf.jasperreports.engine.JasperPrint;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import com.myproject.reports.views.ReportView;
import com.myproject.reports.generator.CustomerInfo; // to pass customer id to the report.
import com.myproject.reports.generator.ReportGen;


public class CSReportAction implements IWorkbenchWindowActionDelegate {
CustomerInfo cusInfo = null;
ReportView rv = null;
private int refreshTimeInMillis = 50000; //Refresh interval


public CSReportAction() {
try {
rv = (ReportView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().
showView(ReportView.ID_REPORT_VIEW);
} catch (PartInitException e) {
e.printStackTrace();
}

}

public void run(IAction action) {
//Calling a dialog which will
//accept customer id from the user
cusInfo = new CustomerInfo(new Shell(), SWT.NONE);
cusInfo.open();
load();

final Display display = rv.getReportViewerComposite().getShell().getDisplay();

//To refresh the screen periodically
display.timerExec(refreshTimeInMillis,new Runnable(){
public void run(){
CSReportAction.this.load();
display.timerExec(refreshTimeInMillis, this);
}
});

}

public void load(){
//Calling the generate report and
//passing the customer id as parameter.
JasperPrint jprint = ReportGen.generateReport(cusInfo.getCustomerInfo());
try {
//Loading the JasperPrint object in the viewer.
rv.getReportViewer().setDocument(jprint);

} catch (Exception e) {
e.printStackTrace();
}
}

public void selectionChanged(IAction action, ISelection selection) {
}


public void dispose() {
}


public void init(IWorkbenchWindow window) {

}
}

Thats it! You are done. When I tried this first, I had a tough time getting it working. But with a lot of help from a friend of mine and the guys at eclipsezone.com, I finally got this working. I hope this tutorial makes it easier for anyone who is trying to achieve the same result.

Ackowledgements:-
My special thanks to the guys who helped me get this working.

1. Haris Peco (http://www.snpe.co.yu)
2. Varghese Cottagiri
3. Nick Edgar

Advertisements
  1. #1 by Anders on November 15, 2005 - 12:33 am

    This made my day. I have struggeled with this and not found any solution. Yours worked straight away. Please note that the code you show for the modified method in the ContextFinder class is a little incomplete the way it comes out on the blog page. It was no problem to figure it out from the original source though. THANKS

  2. #2 by uday on November 15, 2005 - 8:05 am

    Hi anders,
    I was just beginning to think that the whole tutorial was useless.

    Thanks for the comment. Atleast now I know that it is useful to atleast some. This gives me a reason to keep writing. πŸ™‚

    Regards,
    Uday

  3. #3 by rahul on March 17, 2006 - 11:43 am

    Hello Uday
    I am still having problem with this..could you please tell me what needs to be done.

  4. #4 by manish on April 23, 2006 - 6:54 am

    hi uday,
    i am having problems with linking one report to another. i.e. i want to generate a report of all customers and when one clicks on one customer, the personal report of that customer should open.
    i am using iReport to create jaspers, and unable to create even a single hyperlink to http://www.google.com in iReport. pls help

    ( manish.jain.85@gmail.com )

  5. #5 by Nitin K. Verma on May 14, 2007 - 9:50 am

    Hi, Can you send me the whole code so that i will be integrate jasper report into SWT application

  6. #6 by Nitin K. Verma on May 14, 2007 - 9:52 am

    Hi Uday,
    Can you send me the whole code so that i will be integrate jasper report into SWT application . …

  7. #7 by udayms on May 14, 2007 - 2:53 pm

    Hi Nitin,
    I do not have the code base with me now. I dont even have the same dev env with me. Actually, I am working on a different technology these days. But, The post includes the entire code that is needed. The source all the classes are included in the post. Check it out.

    Cheers,
    Uday

  8. #8 by Ramesh on November 19, 2007 - 7:53 am

    Hi uday
    i am using ireport and i don’t know how to get the data using java bean can you give me some example program
    pls its very urgent

  9. #9 by udayms on November 19, 2007 - 8:21 am

    @Ramesh: Hi….. It’s been a really long while since I lask worked with ireport. right now i am working on flex and rias. I do not have any examples in java for the time being. i guess your best bet would be google.com πŸ™‚

  10. #10 by Robbin on August 12, 2009 - 7:13 am

    Orey Uday ga,
    nuvvem chepthunnavo neeku ardam avuthundara first……

  1. syncWordpress 05/29/2009 « Jose Manuel Prieto Palacios
  2. goedkoop verhuisbedrijf

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: