At line 1 removed 2 lines. |
!![JasperReport on AppFuse|JasperReportOnAppFuse] - Putting JasperReports to work on AppFuse. |
|
At line 4 changed 1 line. |
This tutorial will show you how to add a Controller to generate reports from AppFuse user account database. |
This tutorial will show you how to use JasperReports in appfuse based applications. As an example, a report listing all registered users will be created. |
At line 7 changed 4 lines. |
* [1] Create the UserReportController Controller |
* [2] Configuring JasperReports on AppFuse |
* [3] Designing Reports |
* [4] Putting all together |
* [1] Configuring JasperReports on AppFuse |
* [2] Designing Reports |
* [3] SpringMVC integration |
* [4] JSF integration |
At line 12 changed 1 line. |
!!Create the UserReportController Controller [#1] |
!!Configuring JasperReports on AppFuse [#1] |
|
Below are the steps to (<a href="http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseAddLibrary">AppFuseAddLibrary</a>) set up <a href="http://jasperreports.sourceforge.net/">JasperReports</a> on AppFuse. For that, we will include the lastest release version of <a href="http://jasperreports.sourceforge.net/">JasperReports</a>, which in the hour of this writing was 1.2.0. <a href="http://jasperreports.sourceforge.net/">JasperReports</a> itself depends on the following projects: |
|
* BeanShell |
* Commons BeanUtils - it comes with AppFuse |
* Commons Collections - it comes with AppFuse |
* Commons Digester - it comes with AppFuse |
* Commons Logging - it comes with AppFuse |
* iText |
* POI |
|
__1.__ Create a new folder for your library in {{/lib}} using the pattern ''libname-version'' (e.g. {{/lib/iText-1.1}}). |
|
{{ |
/jasper-reports-1.2.0 |
/itext-1.1.4 |
/poi-2.5.1 |
/bsh-2.0b2 |
}} |
|
__2.__ Add all required {{.jar}}'s into the folder you created. (e.g. {{/lib/iText-1.1/iText.jar}}) |
|
{{ |
/lib/jasper-reports-1.2.0/jasperreports-1.2.0.jar |
/lib/jasper-reports-1.2.0/jr-bsh-compiler.jar |
/lib/itext-1.1.4/itext-1.1.4.jar |
/lib/poi-2.5.1/poi-2.5.1.jar |
/lib/bsh-2.0b2/bsh-2.0b2.jar |
}} |
|
__3.__ Edit {{/lib/lib.properties}} to include a section like the following for your library. |
|
{{{ |
# |
# JasperReports - http://jasperreports.sourceforge.net/ |
# |
jasperreports.version=1.2.0 |
jasperreports.dir=${lib.dir}/jasper-reports-${jasperreports.version} |
jasperreports.jar=${jasperreports.dir}/jasperreports-${jasperreports.version}.jar |
}}} |
|
{{{ |
# |
# BeanShell - http://www.beanshell.org |
# |
bsh.version=2.0b2 |
bsh.dir=${lib.dir}/bsh-${bsh.version} |
bsh.jar=${bsh.dir}/bsh-${bsh.version}.jar |
}}} |
|
{{{ |
# |
# Poi - http://jakarta.apache.org/poi/index.html |
# |
poi.version=2.5.1 |
poi.dir=${lib.dir}/poi-${poi.version} |
poi.jar=${poi.dir}/poi-${poi.version}.jar |
}}} |
|
{{{ |
# Itext - http://www.lowagie.com/iText/ |
# |
itext.version=1.1.4 |
itext.dir=${lib.dir}/itext-${itext.version} |
itext.jar=${itext.dir}/itext-${itext.version}.jar |
}}} |
|
__4.__ Add library to the necessary classpaths in {{/properties.xml}}. |
|
{{{ |
<path id="web.compile.classpath"> |
... |
<fileset dir="${jasperreports.dir}" includes="*.jar"/> |
<pathelement location="${itext.jar}"/> |
<pathelement location="${poi.jar}"/> |
</path> |
|
}}} |
|
__5.__ The last step is making sure the library gets added to your deployed webapp. In the {{package-web}} target of {{/build.xml}} look for the {{<war>}} element and add another {{<lib>}} element. |
|
{{{ |
<target name="package-web" depends="compile-web,jsp-2"> |
... |
<lib dir="${jasperreports.dir}" includes="*.jar"/> |
<lib file="${itext.jar}"/> |
<lib file="${poi.jar}"/> |
</war> |
</target> |
}}} |
|
|
!!Designing Reports [#2] |
|
The amused part arrived! With the aid of the designing tool <a href="http://ireport.sourceforge.net/">iReports</a>, we will construct a report based on the list of users of the AppFuse. We will use the lastest release of <a href="http://ireport.sourceforge.net/">iReports</a>, which in the hour of this writing was 1.2.0. It will not be a complex report, only a list of accounts of users with the following columns: |
|
* UserName |
* Name |
* Country |
* Province |
* City |
|
See the result of designing time: |
|
%% |
[http://raibledesigns.com/wiki/attach/AppFuseJasperReports/userreport.png] |
%% |
|
And execution time: |
|
%% |
[http://raibledesigns.com/wiki/attach/AppFuseJasperReports/userreportview.png] |
%% |
|
|
!!SpringMVC integration [#3] |
|
!Create the UserReportController Controller |
At line 18 changed 1 line. |
private transient final Log log = LogFactory.getLog(UserReportController.class); |
private final Log log = LogFactory.getLog(UserReportController.class); |
At line 21 removed 4 lines. |
public UserManager getUserManager() { |
return userManager; |
} |
|
At line 30 changed 3 lines. |
if (log.isDebugEnabled()) { |
log.debug("entering 'userReport' method..."); |
} |
log.debug("entering 'userReport' method..."); |
|
At line 58 changed 2 lines. |
* ParameterMethodNameResolver—Resolves the execution method name based on a parameter in the request__ |
* PropertiesMethodNameResolver—Resolves the name of the execution method by consulting a list of key/value pairs__ |
* ParameterMethodNameResolver—Resolves the execution method name based on a parameter in the request |
* PropertiesMethodNameResolver—Resolves the name of the execution method by consulting a list of key/value pairs |
At line 123 changed 1 line. |
Execute ant test-web -Dtestcase=UserReportController. |
Run ant test-web -Dtestcase=UserReportController. |
At line 125 changed 1 line. |
!!Configuring JasperReports on AppFuse [#2] |
!Putting all together |
At line 127 changed 33 lines. |
<p>Agora os passos necessários (<a |
href="http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseAddLibrary">AppFuseAddLibrary</a>) |
para configurar o <a href="http://jasperreports.sourceforge.net/">JasperReports</a> |
no AppFuse. Para tal, iremos incluir o último lançamento do <a |
href="http://jasperreports.sourceforge.net/">JasperReports</a>, o qual |
na hora desta escrita era 1.2.0. O próprio <a |
href="http://jasperreports.sourceforge.net/">JasperReports</a> depende |
dos seguintes projetos:</p> |
<div class="itemizedlist"> |
<ul type="bullet"> |
<li style="list-style-type: disc;"> |
<p>BeanShell</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>Commons BeanUtils - já vem com AppFuse</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>Commons Collections - já vem com AppFuse</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>Commons Digester - já vem com AppFuse</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>Commons Logging - já vem com AppFuse</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>iText</p> |
</li> |
<li style="list-style-type: disc;"> |
<p>POI</p> |
</li> |
</ul> |
</div> |
Finally we will configure the jrxml file and the resolver for pdf, xsl, csv and HTML formats. |
At line 240 added 209 lines. |
__1.__ Create a new folder for your reports in {{/web/WEB-INF}} with the name {{reports}}. |
|
__2.__ Add the files in the format {{jrxml}} in the directory that you created. |
|
{{{/WEB-INF/reports/userList.jrxml}}} |
|
__3.__ Edit {{/WEB-INF/action-servlet.xml}} file to include the following section for the resolver and view, that we will use. You should have noted that in the UserReportController' method, we're using one parameter to define the format of the report's result at runtime. That is, we will use JasperReportsMultiFormatView view. First the resolver: |
|
[{Java2HtmlPlugin |
|
<!-- If a JasperReports view requires more complex configuration then use the BeanNameViewResolver to map a given view name to a given view bean --> |
<bean id="nameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"/> |
|
}] |
|
And then the view: |
|
[{Java2HtmlPlugin |
|
<bean id="userMultiFormatReport" class="org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView"> |
<property name="contentDispositionMappings"> |
<props> |
<prop key="html">inline; filename=report.html</prop> |
<prop key="csv">inline; filename=report.csv</prop> |
<prop key="pdf">inline; filename=report.pdf</prop> |
<prop key="xls">inline; filename=report.xls</prop> |
</props> |
</property> |
<property name="url" value="/WEB-INF/reports/userList.jrxml" /> |
</bean> |
|
}] |
|
Run ant test-web -Dtestcase=UserReportController again. Make the deploy and access the url /appfuse/userReport.html?action=userReport&format=pdf. AppFuse rocks! |
|
|
!!JSF integration [#4] |
|
!Add a JSF bean to your faces-config.xml |
This bean should be in request scope and have the userManager injected to be able to get the list from the database: |
|
[{Java2HtmlPlugin |
|
<managed-bean> |
<managed-bean-name>reports</managed-bean-name> |
<managed-bean-class>org.appfuse.webapp.action.Reports</managed-bean-class> |
<managed-bean-scope>request</managed-bean-scope> |
<managed-property> |
<property-name>userManager</property-name> |
<value>#{userManager}</value> |
</managed-property> |
</managed-bean> |
|
}] |
|
! JSF Bean implementation |
Depending on requirements we can implement a different behaviour for the bean when the user clicks the report link: |
|
* The report is saved on the server file system for further download. |
* The report is opened directly on the client side without being saved (regenerated every time!). |
|
Here is an implementation for the "saving file" behaviour: |
|
[{Java2HtmlPlugin |
|
package org.appfuse.webapp.action; |
import ... |
public class Reports extends BasePage implements Serializable { |
|
private String format = "pdf"; |
private String WebDir = null; |
|
public String getFormat() { |
return format; |
} |
|
public void setFormat(String format) { |
this.format = format; |
} |
|
public String getWebDir() { |
return WebDir; |
} |
|
// used only in during junit test |
public void setWebDir(String webDir) { |
WebDir = webDir; |
} |
|
public String users() { |
List results = userManager.getUsers(null); |
Map parameters = new HashMap(); |
String ctxPath = getServletContext().getRealPath("/"); |
parameters.put("format", "pdf"); |
if (WebDir != null){ |
parameters.put("WEBDIR", WebDir); |
} |
|
try{ |
// prepare report and data |
InputStream is = getServletContext().getResourceAsStream("/WEB-INF/reports/userList.jrxml"); |
JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(results); |
|
// generate pdf file |
JasperDesign jasperDesign = JRXmlLoader.load(is); |
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); |
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, |
ds); |
JasperExportManager.exportReportToPdfFile(jasperPrint, ctxPath + "/resources/userList.pdf"); |
|
addMessage("errors.detail", "file created: " + ctxPath + "/resources/userList.pdf"); |
} |
catch(Exception e){ |
log.error(e); |
} |
return null; |
} |
|
} |
|
}] |
|
It is pretty difficult to unit test this class correctly and the only thing I could think off was checking that the file is actually generated. Be sure to update the hard coded value in the report to load images from the correct location during the test. |
|
[{Java2HtmlPlugin |
|
package org.appfuse.webapp.action; |
|
import java.io.File; |
|
public class ReportsTest extends BasePageTestCase { |
private Reports bean; |
|
protected void setUp() throws Exception { |
super.setUp(); |
bean = (Reports) getManagedBean("reports"); |
// to use hard coded value in report |
bean.setWebDir(null); |
} |
|
public void testSearch() throws Exception { |
String ctxPath = bean.getServletContext().getRealPath("/"); |
|
// ensures resources directory is present |
File f = new File(ctxPath + "/resources"); |
f.mkdir(); |
|
// generates report |
assertNull(bean.users()); |
|
// checks the file is present |
f = new File(ctxPath + "/resources/userList.pdf"); |
assertTrue(f.exists()); |
|
assertFalse(bean.hasErrors()); |
} |
|
} |
|
}] |
|
|
Here is the implementation for the "open in browser" behaviour: |
|
[{Java2HtmlPlugin |
|
package org.appfuse.webapp.action; |
import ... |
public class Reports extends BasePage implements Serializable { |
|
public String users() { |
List results = userManager.getUsers(null); |
Map parameters = new HashMap(); |
parameters.put("format", "pdf"); |
parameters.put("WEBDIR", getServletContext().getRealPath("/")); |
try{ |
InputStream is = getServletContext() |
.getResourceAsStream("/WEB-INF/reports/userList.jrxml"); |
JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(results); |
getResponse().setContentType("application/pdf"); |
getResponse().addHeader("Content-Disposition", "attachment; filename=userList.pdf"); |
JasperDesign jasperDesign = JRXmlLoader.load(is); |
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); |
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, ds); |
JasperExportManager.exportReportToPdfStream(jasperPrint, getResponse().getOutputStream()); |
getFacesContext().getApplication().getStateManager() |
.saveSerializedView(getFacesContext()); |
getFacesContext().responseComplete(); |
} |
catch(Exception e){ |
log.error(e); |
} |
return null; |
} |
} |
|
}] |
|
I have no unit test for this class, if you have one let us know. |
|
|
!Add a link to generate the report on the jsp page |
It is as simple as : |
|
[{Java2HtmlPlugin |
|
<h:commandLink value="get pdf report" action="#{reports.users}"/> |
|
}] |