Friday, December 19, 2008

Integrating the Birt Viewer into OpenReports using Struts Part 2

In the last mini-tutorial, we've integrated the BIRT Ajax Viewer 2.3.1 servlet
into OpenReports 3.1, and updated the BIRT libraries from 2.3 to 2.3.1.
In this section, we wrap up the tutorial by basically modifying
the OpenReports UI such that when a BIRT Report is run, we'll get
a UI that allows the additional enhancements:

1. Execute the current BIRT rptdesign report using the BIRT Viewer in the Same Window
2. Execute the current BIRT rptdesign report using the BIRT Viewer in a new Window
Futhermore , we can specify if the BIRT Viewer will be using the Full Window,
or if it will be embeded showing us the Report Name and Path to the Design file.
3. Adding Postscript and Word Export options for BIRT Reports (OpenReports is missing this)

Here are some screenshots of the enhancement and a link to the modified source code here-  openReportsWithBIRT2.3.1.zip  
From OpenReports


From OpenReports



I've highlighted the Java files that I've modified using the original OpenReports source code. The perspective below is from Eclipse Ganymede 3.4 which I'm working with.

From OpenReports


We modify struts.xml by adding a new Action called
birtRunReport and create an action class in org.efs.openreports.actions.BirtReportAction.java that extends ActionSupport.
(I've basically mimicked what I've seen with the existing OpenReport Actions
since I'm fairly new to Struts )







ReportOptions.jsp


reportRun


queryReport


chartReport


jxlsReport


jpivot.action


reportSchedule.action


listScheduledReports.action


birtRunReport





BirtViewer.jsp




Here's the BirtReportAction.java class that I wrote that is used in BirtViewer.jsp where we use
the birt tag library (birt.tld) to run the Viewer.
The birt_isHostPage is an attribute used to indicate if the Viewer will use the whole page.


package org.efs.openreports.actions;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

import java.util.Date;
import java.util.Map;
import org.apache.log4j.Logger;
import org.efs.openreports.ORStatics;
import org.efs.openreports.engine.BirtReportEngine;
import org.efs.openreports.engine.ReportEngine;
import org.efs.openreports.engine.ReportEngineHelper;
import org.efs.openreports.engine.input.ReportEngineInput;
import org.efs.openreports.objects.ORProperty;
import org.efs.openreports.objects.Report;
import org.efs.openreports.objects.ReportLog;
import org.efs.openreports.objects.ReportUser;
import org.efs.openreports.providers.DataSourceProvider;
import org.efs.openreports.providers.DirectoryProvider;
import org.efs.openreports.providers.PropertiesProvider;
import org.efs.openreports.providers.ReportLogProvider;
import org.efs.openreports.util.LocalStrings;
import org.efs.openreports.util.ORUtil;

public class BirtReportAction extends ActionSupport
{
private static final long serialVersionUID = 7473180642590984527L;
protected static Logger log = Logger.getLogger(BirtReportAction.class);
private Map<Object, Object> session;
private String birt_isHostPage; // see birt.tld

private DirectoryProvider directoryProvider;

private Report report;
private String designFile;

public String execute() throws Exception
{
report = (Report) ActionContext.getContext().getSession().get(ORStatics.REPORT);
designFile = directoryProvider.getReportDirectory() + report.getFile();
log.info("BirtReportAction chained - directoryProvider:" + directoryProvider.getReportDirectory());
log.info("BIRT Report Action , Report File:" + report.getFile() );
log.info("BIRT Report Action , Report Name:" + report.getName() );
log.info("BIRT Report Action ), DESIGN FILE:" + designFile );
log.info("BIRT Report Action , birt_isHostPage:" + birt_isHostPage);
return SUCCESS;
}

public String getDesignFile()
{
return designFile;
}

public Report getReport()
{
return report;
}

public void setBirt_isHostPage(String birt_isHostPage)
{
this.birt_isHostPage = birt_isHostPage;
}

public String getBirt_isHostPage()
{
return birt_isHostPage;
}



public void setDirectoryProvider(DirectoryProvider directoryProvider)
{
this.directoryProvider = directoryProvider;
}

@SuppressWarnings("unchecked")
public void setSession(Map session)
{
this.session = session;
}

}

There are 2 JSP's that need to be added/modified that contribute to the UI.
The first is ReportOptions.jsp which is the result of the action associated
with clicking on a BIRT Report.

<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://displaytag.sf.net" prefix="display" %>
<?xml:namespace prefix = s /><s:include value="Banner.jsp"></s:include>
<s:actionerror></s:actionerror>
<script language="JavaScript" type="text/JavaScript">
function setDefaultExportType()
if (optionsForm.exportType.length)
{
optionsForm.exportType[0].checked=true
}
else
{
optionsForm.exportType.checked=true
}
}

function setBlankTarget()
{
optionsForm.target="_blank";
}

function setNoTarget()
{
optionsForm.target="";
}


function setBlankTargetBirt()
{
birtForm.target="_blank";
}

function setNoTargetBirt()
{
birtForm.target="";
}

</script>

<div align="center">

<a class="back-link img-report-small" href="http://www.blogger.com/reportList.action"><s:text name="link.back.reports"></s:text></a>
<a class="back-link img-group-small" href="http://www.blogger.com/reportGroup.action"><s:text name="link.back.groups"></s:text></a>





<div class="img-export important" id="instructions" style="WIDTH: 70%">
<s:text name="reportOptions.title"></s:text> <s:property value="report.name"></s:property>
</div>

<form class="dialog-form" style="WIDTH: 75%" name="optionsForm" action="reportOptions.action">
<table class="dialog">
<tbody><tr>
<td class="boldText"><s:text name="reportOptions.exportType"></s:text></td>
<s:if test="report.pdfExportEnabled">
<td>
<input type="radio" value="0" name="exportType">PDF
</td>
</s:if>
<s:if test="report.htmlExportEnabled">
<td>
<input type="radio" value="2" name="exportType">HTML
</td>
</s:if>
<s:if test="report.csvExportEnabled">
<td>
<input type="radio" value="3" name="exportType">CSV
</td>
</s:if>
<s:if test="report.xlsExportEnabled report.jXLSReport">
<td>
<input type="radio" value="1" name="exportType">XLS
</td>
</s:if>
<s:if test="report.rtfExportEnabled">
<td>
<input type="radio" value="5" name="exportType">RTF
</td>
</s:if>
<s:if test="report.textExportEnabled &amp;&amp; report.birtReport">
<td>
<input type="radio" value="6" name="exportType">Text
</td>
</s:if>
<s:if test="report.excelExportEnabled">
<td>
<input type="radio" value="7" name="exportType">Excel
</td>
</s:if>
<s:if test="report.jasperReport &amp;&amp; report.imageExportEnabled">
<td>
<input type="radio" value="4" name="exportType">Image
</td>
</s:if>
<!-- enhancements to support BIRT export types for BIRT 2.3.1 -->
<s:if test="report.birtReport">
<td>
<input type="radio" value="9" name="exportType">MS DOC
</td>
<td>
<input type="radio" value="10" name="exportType">PPT
</td>
<td>
<input type="radio" value="11" name="exportType">Postscript
</td>
</s:if>

<script language="JavaScript" type="text/JavaScript">
setDefaultExportType()
</script>
</tr>
</tbody></table>




<div class="button-bar" id="buttons">

<input class="standardButton" onclick="setNoTarget()" type="submit" value="<s:text name=" name="submitRun">">
<input class="standardButton" onclick="setBlankTarget()" type="submit" value="<s:text name=" name="submitRun">">
<s:if test="user.scheduler">
<input class="standardButton" onclick="setNoTarget()" type="submit" value="<s:text name=" name="submitSchedule">">
</s:if>


<s:if test="report.birtReport">
<td>
<input class="standardButton" onclick="setBlankTarget()" type="submit" value="<s:text name=" name="submitBirtReport">">
</td>
</s:if>

</div>

</form>




<!-- BIRT AJAX Viewer Form -->
<s:if test="report.birtReport">
<div class="img-export important" id="instructions" style="WIDTH: 70%">
BIRT AJAX-Based Report Viewer Output
</div>

<form class="dialog-form" style="WIDTH: 75%" name="birtForm" action="reportOptions.action">
<table class="dialog">
<tbody><tr>
<td class="boldText"><s:text name="View Options"></s:text></td>
<td>
<input type="radio" value="true" name="birt_isHostPage">Full Window
</td>
<td>
<input type="radio" value="false" name="birt_isHostPage">Embedded
</td>
</tr>
</tbody></table>



<div class="button-bar" id="buttons">
<input class="standardButton" onclick="setNoTargetBirt()" type="submit" value="<s:text name=" name="submitBirtReport">">
<input class="standardButton" onclick="setBlankTargetBirt()" type="submit" value="<s:text name=" name="submitBirtReport">">
</div>
</form>

</s:if>

</div>
<s:include value="Footer.jsp"></s:include>





The second JSP we require to modify is BirtViewer.jsp . This is the page
that calls the BIRT Viewer. We use the BIRT Tag library to show the viewer. You can read about it here from BIRT Eclipse site under Using the BIRT Report Viewer


<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://displaytag.sf.net" prefix="display" %>
<%@ taglib uri="/birt.tld" prefix ="birt" %>
<!-- refer here for documentation http://struts.apache.org/2.x/docs/tag-reference.html -->

<html>
<head>


<link href="styles/iv/index.css" type="text/css" rel="stylesheet">
<link href="http://www.eclipse.org/images/eclipse.ico" type="image/x-icon" rel="shortcut icon">
<style>
.warningMessage { color:red; }
</style>



<s:textfield label="Report Name" name="report.file"></s:textfield>


<s:textfield label="Design File Path" name="designFile"></s:textfield>
<?xml:namespace prefix = birt /><birt:viewer id="birtViewer" format="html" width="700" height="450" pattern="frameset" reportdesign="${designFile}" ishostpage="${birt_isHostPage}">
</birt:viewer>



That's the core of the work. There are other a few other Java files
that need to be modified to get this example working - they only
involve very minor changes, so I'll just present some snit bit code Fragments.

In org.efs.openreports.actions.ReportOptionsAction.java,
we need to pass the attribute birt_isHostPage and execute
th Birt Viewer action.


if (report.isBirtReport() && !submitSchedule)
{
log.info("This is a BirtReport (aong)");
if(submitBirtReport) {
log.info("Submitting BIRT REPORT(aong)");
if (birt_isHostPage != null){
log.info("birt_isHostPage" + birt_isHostPage);
} else {
log.info("birt_isHostPage" + birt_isHostPage);
}

return ORStatics.BIRT_VIEWER_ACTION;
}
}



The action is defined in org.efs.openreports.ORStatics.java
as

public static final String BIRT_VIEWER_ACTION = "birtReport";


In org.efs.openreports.ReportConstants, we need
to define the new Export Types "DOC", "PPT", and "POSTSCRIPT".

Lastly, we need to add support to the Export types that OpenReports does not
support that the BirtEngine handles in org.efs.openreports.engine.BirtReportEngine.java


if (input.getExportType() == ExportType.DOC)
{
output.setContentType(ReportEngineOutput.CONTENT_TYPE_MSWORD);
renderOption.setOutputFormat("doc");
}

else if (input.getExportType() == ExportType.PPT)
{
output.setContentType(ReportEngineOutput.CONTENT_TYPE_POWERPOINT);
renderOption.setOutputFormat("ppt");
}

else if (input.getExportType() == ExportType.POSTSCRIPT)
{
output.setContentType(ReportEngineOutput.CONTENT_TYPE_POSTSCRIPT);
renderOption.setOutputFormat("postscript");
}

.

That's about it - bundle the package up , build it and deploy it to Tomcat.

I've enclosed the source code here.  There are two folders enclosed.
1. src folder -> contains all java classes, struts.xml
2. WebRoot -> Tomcat context files.  

*Note* - I've stripped down the WebRoot/WEB-INF folder.
It is missing the lib folder jars that you can get from openReports
(OpenReports 3.1) and the platform folder which contains the BIRT plugins which you can
get from BIRT .   (BIRT 2.3.1 Runtime).  

Download link: openReportsWithBIRT2.3.1.zip  (src files only)

2 comments:

  1. Thanks, it is a very nice doc, I have tried the way as you described. But in last stage, I got this Exception. There is no anyway I can find the logs for it. Where should I setup the session parameter for the viewservlet?

    '
    The viewing session is not available or has expired.'

    ReplyDelete