Developer Guide Mireport": February 14th 2010 Author: Florian Müller
Developer Guide Mireport": February 14th 2010 Author: Florian Müller
Version 1.0
richability
http://www.richability.com
Summary
What is mireport?
Reporting is one of the most important points when developing applications, it is nice to store the quarterly company results within your application in order to simply use them, but it is even nicer if you could simply export this data to PDF/Word/xyz in order to present them during a meeting. Adobe Flex offers several nice reporting components within your application, unfortunately you can not export this data (except you wanna pay +/-100.000 $ in order to use LiveCycle Data Service, PDF reporting is part of LCDS). So either you have to launch the application during your meeting or take some screenshots - but anyway nobody will be able to use this data as it is in a non-Excle/Word/xyz format so you end up in typing in the data to your own Excel sheet.
Nice Flex reports but how to use this data in an Excel sheet for example?
On the server side there is a proven technology called JasperReports (http://jasperforge.org/projects/jasperreports) which enables you to adress this issue - creating reports based on data stored in your database to whatever format. But how to connect JasperReport on the server and how to push the server generated reports to your Flex application? Thats exactly where mireport comes in, mireport is a simple Flex - JasperReport - interface which allows you to pass paramteres from your Flex application to JasperReports and returns a nice looking report. Currently mireport supports PDF, XLS and RTF output (see screenshot below).
1.2 Concepts
As already said, JasperReport is the heart of the whole report generation, JasperReport has nothing to do with Flex, its a core Java based reporting engine. Of course any report requires data, so JasperReport does not create .pdf/.xls etc. only, JasperReport links in the data to the report as well. In a nuthsell: you dene a report layout and a query, JasperReport does the rendering and mixes in the data, fetched from the database directly. See the following scribble for assertion.
Ok, you got it? The developer somehow (we will talk about this in the next paragraph) denes how the report should look like and which data in terms of which query is tied into the report. This information is passed to the JasperReport Engine which actually querries the database and renders the report. Thats the core of all and we have encapsulated the core in order to save your time and money... As already mentioned, the developer somehow needs to bundle the report layout data and the query behind. Thats what iReport does, iReport enables developers to create reports via Drag & Drop instead of coding Jasper XML. So your starting point, before you tie in our report component to your Flex application is always iReport.
Now that you got the core idea of JasperReport and iReport lets start to chalk out the big picture. Lets take a look on the mireport component within the context of a Flex application. Please have a look into the following gure:
The mireport component is embedded into a default Flex application. The component is initialized with several parameters (we will discuss these in detail when starting our example), these parameters cause the component to contact the server. On the server side a mireport Servlet handles the incoming requests, extracts parameters and triggers report generation. The report is returned as a link to the application, the link is resolved immediately and the report is being displayed. Why do we return a link instead of returning PDF/RTF/XLS data directly? Well, the reason is that browsers behave totally different when receiving RTF data for instance (books could be published with this so called MIME type browser behavior...). Some browsers might open the result directly, others re up an applications and some do not display RTF at all. So we can not guarantee what happens with the result. But what we can guarantee: HTML is always displayed perfectly - so we simply return HTML (this will be displayed within the preview) and links to the PDF/RTF/XLS les generated on the server. The user can press one of the icons and dependent on the browser behavior the le will be opened. Or the user presses right mouse - save target as in order to save the source of the link. But you should not care about those details as we already cared for them, so you simply can use our component as is. The important point is the servlet and the Flex mireport component: these talk to each other and return the desired report to the Flex client. Of course static querries are boring as you are able to receive one data view only: Select * from User will always return all users - but what if you want all users aged > 20 in your report? JasperReport enables you as well to create reports with parameter based querries, for example you could create a report based on a query called Select * from User where age > $parameter and pass in the parameter - dependent on what you pass in the report result will change. The mireport component allows you to pass in query parameters as well, we will see this during the example - so you are not limited to static reports, your reports differ dependent on the data passed in from the client.
2 mireport by example
Now that you got the backgrounds we simply start using the component, you will see usage is pretty straight forward and benet to your application is immensly.
2.1 Prerequisits
The following applications are required to be installed/available on your machine: Flex/Flash Builder iReport Application Server (we will be using Tomcat) Database to query against (we will be using MySQL) JasperReport jar les (we packaged these to our bundle, folder called extrenal libs is available) mireport.swc
1/8: launch iReport and select File - New, select a template (we use the template called Coffee Landscape) and launch the report wizzard by select Launch Report Wizzard in the bottom of the screen;
In the next Wizard screen you have to specify a name for the report, lets use myreport. You have to specify a destination as well, make sure you remember the destination as we will require the destination later on.
Next step is denition of datasource. Select the New button in order to create a new datasource for your report, another Wizard launches which will guide you through the process of creating a new datasource:
Specify the database credentials (DB user, DB URL etc.) and press save button:
Ok, back to the originally launched wizard you should be able to pick your newly created datasource now:
You have to specify a report query, of course this query depends on your underlying database. Before you actually start with passing parameters we suggest to start with a simple SELECT * FROM YourTable, in your case YourTable is called Item, a table holding several item positions... mireport Developers Guide 8
The next upcoming dialog can be used to check whether datasource etc. have been specied correctly, if so iReport asks you which columns you want to include to your report. We include all columns except the image column:
The next (nearly last!) dialog will ask you for grouping, we do not specify any grouping:
Last you can nally create the report by pressing the nish button, the repoirt structure will be displlayed within the iReport application
10
Now you can adapt the report (changing title, adding images etc.), as already mentioned: iReport allows you to do a lot within the space of reporting. You can easily create a report on your own without using any templates, we just used the template approach in order to get fastly some stuff which is working...
11
Ok, you got the idea of generation? Then, time has come to smoehow push the report to the client and trigger report generation by the client instead of using the Preview button.
12
Select Java instead of Groovy, save your changes and double check whether your report is still working by switiching to the preview again, in 99% of all reports report generation will fail, you can check the error window in the bottom of iReport there you will nd the cause for the error, iReport will give you message similar to this one:
This is really a nasty error as this error would not occur if iReport was using Java as basic language instead of Groovy stuff. Anyway, to x this issue, double click the error and the expression builder shows displaying the expression causing the error:
13
In our case an int value is expected but Integer is used: $V{REPORT_COUNT}%2 == 0 So you have to cast the Integer to an int value by appending the following Java afunction: $V{REPORT_COUNT}.intValue()%2 == 0
Now this error should be resolved, often there occurs a second casting error, but simply try whether generation works now by switching to the preview mode. It might fail again by complaining about a boolean/Boolean cast, again double click the error and you might nd something similar to this in the Expression Builder: $V{REPORT_COUNT}.intValue()%2 == 0 This needs to be changed to a Boolean value as a Boolean value is expected: Boolean.valueOf($V{REPORT_COUNT}.intValue()%2 == 0)
When creating a MXML le in your project and switiching to the design mode you should see there our new component called Report:
14
Notice: there is another component called iFrame which also appears in the component panel. mireport heavily uses this component, basically all report visualization is based on an iframe concept. The iframe component is a sparate component, free of charge without any usage limitations, the original component is hosted at http://code.google.com/p/ex-iframe/. Simply drag the Report component to your application, the report component appears on your stage and you can position the component same way you usually do with Flex components.
15
The component is ready to be congured now (you have to pass in the report being rendered, Server adress etc.), before we actually start the conguration work the server part is hooked in.
<servlet-class>com.richability.ireport.servlet.CredentialServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CredentialServlet</servlet-name> <url-pattern>/CredentialServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>PDFServlet</servlet-name> <servlet-class>com.richability.ireport.servlet.PDFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PDFServlet</servlet-name> <url-pattern>/PDFServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>RTFServlet</servlet-name> <servlet-class>com.richability.ireport.servlet.RTFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RTFServlet</servlet-name> <url-pattern>/RTFServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>XLSServlet</servlet-name> <servlet-class>com.richability.ireport.servlet.XLSServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>XLSServlet</servlet-name> <url-pattern>/XLSServlet</url-pattern> </servlet-mapping> <!-- JasperReport HTML image handling --> <servlet> <servlet-name>ImageServlet</servlet-name> <servlet-class>net.sf.jasperreports.j2ee.servlets.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ImageServlet</servlet-name> <url-pattern>/reportImage</url-pattern> </servlet-mapping> <!-- Tidy up - deletion of generated reports when session is closed --> <listener>
<listener-class>com.richability.ireport.servlet.SessionListener</listener-class> </listener> <!-- JasperReport servlet configuration end --> mireport Developers Guide 17
Ok, youre almost close to it! In the very early beginning of this guide a report has been created via iReport (we kindly asked you to remember where this report is stored, now time has come to lookup this report again!). This report will be used on the serverside for report generation, so we have to copy this report to the server part of the project. In order to ensure reports are found and looked up correctly you have to create a folder called reports on the same level your WEB-INF folder resides (in one of the next releases we provide path adaption, for the rst release you have to use this static approach which saves you from additional conguration efforts). DO NOT CREATE A FOLDER ON YOUR OWN! Instead of this we prepared a folder for you within the build directory called reports. Copy and paste this folder. Reason for using this folder: this folder contains three images which will be used in the iframes. These images are required, otherwise you wont see PDF/Word/Excel buttons in the report component.
One more thing about images (as the report we have created uses images, the coffee beans). If you use images, iReport will put these to the same level your .jasper le resides. As the images need to be available as well you have to copy the images used in your report as well to the reports folder. A dedicated Jasper servlet will lookup images and embed these to your report, so it is important to have the images in the report folder as well (see the coffee.jpg and coffee_stain.png le). Dont mix the report images with our images in the images folder. The images folder has nothing to do with the images used within your report! Serverside conguration is nished now, in case your application uses several reports simply drop these to the reports folder.
does...): <ns1:Report x="37" y="31" databaseURL="jdbc:mysql://localhost/warehouse" databaseUser="root" databasePassword="" driver="com.mysql.jdbc.Driver"> 2) Add the server host as the Flex component needs to know where report generation is executed: <ns1:Report x="37" y="31" databaseURL="jdbc:mysql://localhost/warehouse" databaseUser="root" databasePassword="" host="http://localhost:7070/mireportUsage/"> 3) Add the report you want to display within the component: <ns1:Report x="37" y="31" databaseURL="jdbc:mysql://localhost/warehouse" databaseUser="root" databasePassword="" host="http://localhost:7070/mireportUsage/" reportFile="myreport.jasper"> These basic parameters are always required - for additional parameters (text adaption etc.) please refer to the ASDoc. By the way, you are ready to report now! Deploy the application and you will see a nice JasperReport being renered within the component!
19
3 HowTos
Here we have put several HowTos allowing you to use the report component more efciently.
A whole Switch Report example might look like this: <local:Report id="reportComponent" x="37" y="31" databaseURL="jdbc:mysql://localhost/warehouse" databaseUser="root" databasePassword="" driver="com.mysql.jdbc.Driver"
20
host="http://localhost:7070/mireportUsage/" reportFile="myreport.jasper"> </local:Report> <s:Panel x="737.95" y="34.3" width="250" height="200" title="Adapt report"> <s:Label x="10" y="15" text="Select report"/> <mx:ComboBox id="cb_report" x="100" y="10" editable="true" width="140" change="{onReportChange()}"> <mx:dataProvider> <fx:Array> <fx:String>Coffee</fx:String> <fx:String>Cherry</fx:String> <fx:String>Flowers</fx:String> </fx:Array> </mx:dataProvider> </mx:ComboBox> </s:Panel> <fx:Script> <![CDATA[ private function onReportChange():void { if (cb_report.text == "Coffee") { reportComponent.reportFile = "myreport.jasper"; } if (cb_report.text == "Cherry") { reportComponent.reportFile = "cherry.jasper"; } if (cb_report.text == "Flowers") { reportComponent.reportFile = "flower.jasper"; } } ]]> </fx:Script>
21
Title text: reportTitle property Available Report Formats Text: descriptionText property Hint text: hintText property Whenever these properties change the report component will directly reect these changes. The following example gives you an idea on changing these properties: ... .. . <s:Label x="10" y="69" text="Set title"/> <s:Label x="10" y="99" text="Set left text"/> <s:TextInput x="100" y="59" width="140" id="txt_title"/> <s:TextInput x="100" y="90" width="140" id="txt_reportFormats"/> <s:Label x="10" y="133" text="Set hint text"/> <s:TextInput x="100" y="121" width="140" id="txt_hint"/> <s:Button x="173.05" y="151" label="Apply" width="67" click="onApply()"/> <mx:HRule x="10" y="43" width="230"/> <mx:HRule x="9" y="184" width="230"/> ... // apply text values... mireport Developers Guide 22
If the user presses the button the component width will be increased (920px width, 450px height, Landscape mode) or decreased (670px width, 450px height, Portrait mode).
23
If you want to deny access to these user triggered sizing activieties you can do so by setting the property displayLandscapePortraitSwitch to false, the icon wont be displayed then:
There is one more option regarding landscape/portrait mode, you can set the mode (=width of the component) on your own by setting the reportFormat property either to landscape or portrait: // landscape/portrait switch... private function onChangeFormat():void { if (rdg_format.selectedValue == "portrait") reportComponent.reportFormat = "portrait"; else reportComponent.reportFormat = "landscape"; } This is typically used when you take over control regarding width of the component, for example if you want to display a report and know exactly: this report is a landscape report, so I have to switch the component to landscape directly.
3.4 HowTo set a custom tooltip text for the landscape/portrait button
You can overwrite the default tooltip text for the landscape/portrait button by setting the protraitTooltipText or landscapeTooltipText property: // apply tooltips mireport Developers Guide
24
4 Passing parameters
Of course static reports are boring (and useless) as you usually want to display data based on ltering for age, name...etc.. Our component allows you to pass in an arbitrary number of parameters from the client to the server which then will be used to query against the database.
Next, select the paramter and adapt the parameter properties in the right hand property editor, as ID is an Integer value the parameter will be type Integer as well, name of the lter is idFilter. Finally the report query needs to be adapted, open the query editor and adapt the query as following:
The query should be something like this: Select * from item where itemId > $P{idFilter} You see, the newly added parameter is used within the report query, switch to the preview mode and iReport will prompt for a paramter value in order to test the query.
26
Adding a String parameter: // create a 2nd filter // property ("descriptionFilter") // String filter var kv2:KeyValuePair = new KeyValuePair(); kv2.addStringPair("descriptionFilter",txt_filterDescription.text); filters.addItem(kv2); Adding a date parameter: // create a 3rd/4th filter // property ("nextDeliveryFilterMin")/("nextDeliveryFilterMax") // Date filter var kv3:KeyValuePair = new KeyValuePair(); kv3.addDatePair("nextDeliveryFilterMin",dat_min.selectedDate); filters.addItem(kv3); Adding Float parameter (datatype Number will always be translated to Java Float): // create a 5th filter // property ("quantityFilter") // Float filter var kv5:KeyValuePair = new KeyValuePair(); mireport Developers Guide 27
kv5.addNumberPair("quantityFilter",new Number(txt_filterQuantity.text)); filters.addItem(kv5); The lters are passed to the report and used for report generation. The corresponding JasperReport query could look like this: Select * from item where itemId > $P{idFilter} and itemDescription LIKE $P{descriptionFilter} and (nextDelivery between $P{nextDeliveryFilterMin} and $P{nextDeliveryFilterMax}) and itemQuantity > $P{quantityFilter} And the parameters are passed in via Flex frontend. Being honestly this seems to be little bit complex, but we suggest: simply start with a query containing single String lter and extend this example, as once understood passing lters is really easy and one of the most powerful features of mireport!
Take care: all lter parameters will be passed to the servlet via URL - which means to you that special attention to special characters! For example if you want to pass the % sign (often used in LIKE query lter parameters) to your report you have to pass %25 instead of using % only. To make things easier we just dropped a table below containing frequently used special characeters.
28
5 Advanced topics
5.1 Serverside le generation
As you might have noticed, JapserReport generates a physical le which needs to be placed somewhere on your server. We congured JasperReport to put this le in your report directory, we add a unique ID in order to make sure several reports can be used by several users. So the tricky point/question is: how to get rid of these les as the user usually requires these les only one time, The answer: we have implemented a pretty tricky le handling, for any le which is generated during a session (session in terms of one client being connected to the server) we put a little hint to the session. If the session terminates (server timeout, client inactivity) we read this information and start deleting the corresponding les. The only exception where the les might reside within the directory: you shut down the server as we have no chance to gather this information in this case. If this should happen you might have to clean the report directory manually. You could also write a batch job checkking any 24 hours for .pdf/.rtf/.xls les older than 24 hours and remove these...but we thought the session way is much easier for you;
6 Known Issues
There is a serious issues which occurs when using mireport with IE 7: the report is displayed once, as soon as the user clicks somewhere outside the report area the report disappears. This is a focus management problem which occurs when using the iFrame component, but there is a workaround in order to get rid of this begaviour: In your project (where you want to integrate the mireport component), open the index.template.html le, the le can be found within the html-template folder. Add the following snippet to one of the JavaScript sections starting with the <script> tag: function setBrowserFocus(){document.getElementById('${application}').focus();} The full script passage now should look like this (see red entry): <script type="text/javascript"> <!-- For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection. --> var swfVersionStr = "${version_major}.${version_minor}.${version_revision}"; <!-- To use express install, set to playerProductInstall.swf, otherwise the empty string. --> var xiSwfUrlStr = "${expressInstallSwf}"; var flashvars = {}; var params = {}; params.quality = "high"; params.bgcolor = "${bgcolor}"; params.allowscriptaccess = "sameDomain"; params.allowfullscreen = "true"; var attributes = {}; attributes.id = "${application}"; mireport Developers Guide
29
attributes.name = "${application}"; attributes.align = "middle"; swfobject.embedSWF( "${swf}.swf", "flashContent", "${width}", "${height}", swfVersionStr, xiSwfUrlStr, flashvars, params, attributes); <!-- JavaScript enabled so display the flashContent div in case it is not replaced with a swf object. --> swfobject.createCSS("#flashContent", "display:block;text-align:left;"); function
setBrowserFocus(){document.getElementById('${application}').focus();}
</script>
Last but not least you have to call this newly added method after your application has nished loading, for example triggered by a creationComplete Event: creationComplete="{ExternalInterface.call('setBrowserFocus');}"
Following these steps the IE issue will be resolved. Unfortunately we can not embed this routine to our component, otherwise we are stealing the focus from your application...
30