Chart localization in JasperReports - jasper-reports

I'm using JasperReports 5.5.1 to generate reports from Java. I designed the reports using Jaspersoft Studio. The report has several charts of different types and I have some problems when trying to localize the report using a ResourceBundle passed in the REPORT_RESOURCE_BUNDLE parameter. All the texts in the report are translated correctly except for the ones in the charts. I filled the keyExpression, labelExpression and seriesExpression with $R{STRING_KEY}, but the report is filled with the STRING_KEY instead of its value in the properties file. The title of the charts though is correctly translated.
Can anyone help me with this issue?

I found a solution.
First create a class responsible for getting the localized string from a specific resource bundle.
public class ReportLocalizer {
private static String resourceBundleBaseName = "com.company.package.boundle_name";
public static String getLocalizedString(Locale locale, String key) {
ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleBaseName, locale);
return resourceBundle.getString(key);
}
}
Set the desired locale using JasperReports' parameters:
Map<String, Object> parameters = new HashMap<String, Object>();
...
parameters.put(JRParameter.REPORT_LOCALE, locale);
...
final JasperPrint print = JasperFillManager.fillReport(report, parameters, datasource);
To translate a string in a chart, pass it together with the report's locale to ReportLocalizer's getLocalizedString method.
[CDATA[com.company.package.ReportLocalizer.getLocalizedString($P{REPORT_LOCALE}, "string_key")]]>
For example, we can localize a pie chart showing the amount of men and women this way:
<pieChart>
<chart>
...
</chart>
<pieDataset>
<dataset>
...
</dataset>
<keyExpression><![CDATA[com.company.package.ReportLocalizer.getLocalizedString($P{REPORT_LOCALE}, $F{gender}.toString())]]></keyExpression>
<valueExpression><![CDATA[$F{amount}]]></valueExpression>
<labelExpression><![CDATA[String.valueOf($F{amount})]]></labelExpression>
</pieDataset>
<piePlot>
...
</piePlot>
</pieChart>

Related

Generating password protected pdf. How to get unique password for every user?

I've uploaded my reports on JasperServer where I'm scheduling the reports and send the pdf as attachments in emails to the users using the jobs rest api. Everything works perfectly, however we also need the pdf's to be encrypted. I've read the wiki topic and was able to encrypt the pdf.
But we want the passwords to be dynamic and be different for every user(for exmaple some combination of their phone numbers and date of births). The example described in the link specifies the password as a report property in the jrxml.
<property name="net.sf.jasperreports.export.pdf.user.password" value="123456"/>
<property name="net.sf.jasperreports.export.pdf.owner.password" value="123456"/>
The password is specified as a string and is similar for every pdf generated from this jrxml.
I tried something like this
<property name="net.sf.jasperreports.export.pdf.user.password" value="{$F{dateOfBirth}}"/>
where $F{dateOfBirth} is the dateOfBirth of the user for which the query is being run. But instead of putting in the field value, it just considers it a string and sets the password to="{$F{dateOfBirth}}"
How do I go along with this? Is their any way for me to set different passwords for every user?
NOTE:The datasource is configured for the report on the jasperserver. On the job execution api call, Jasperserver executed the query, fills the report, exports as pdf and sends it as email to the user.
Add the following code in your Java code.
JasperPrint print = JasperFillManager.fillReport(jasper, parameters, beanColDataSource2);
print.setProperty("net.sf.jasperreports.export.pdf.user.password", "jasper123");
Add in JRXML.
property name="net.sf.jasperreports.export.pdf.encrypted" value="True"
property name="net.sf.jasperreports.export.pdf.128.bit.key" value="True"
property name="net.sf.jasperreports.export.pdf.permissions.allowed" value="PRINTING"
As one comment mentioned, just use Java.
Here is an example, how I would code this (it's not perfect, but I think you will get it):
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
import net.sf.jasperreports.engine.fill.JRFillParameter;
public class GetBirthdayScriptlet extends JRDefaultScriptlet {
private Connection conn;
private Connection getConnection() throws JRScriptletException {
if (conn == null) {
if (getParameterValue(JRFillParameter.REPORT_CONNECTION) != null) {
conn = (Connection) (getParameterValue(JRFillParameter.REPORT_CONNECTION));
} else {
throw new RuntimeException("No db-connection configured in the report!");
}
}
return conn;
}
public String getBirthday(String email) throws JRScriptletException, SQLException {
ResultSet result = null;
String resultString = null;
CallableStatement stmt = getConnection().prepareCall("select birthday from birthday_table where email = " + email);
stmt.executeUpdate();
result = stmt.getResultSet();
if(result.next()){
result.getString(1);
}
return resultString;
}
}
Pack this little snippet into a jar and add it to your Studio Build Path and also upload it to your Jaspersoft Server.
In your report outline rightlick on Scriptlets -> "Create Scriptlet"
The class of the scriptlet is GetBirthdayScriptlet (this is the codesnippet-class).
The expression you want to use in your report is:
$P{>>scriptlet-name<<_SCRIPTLET}.getBirthday("email#example.com")
Instead of entering the String, just use the parameter.
Also, maybe think of using the Jaspersoft Built In Parameter LoggedInUserEmailAddress
This helps if you want live-reports to be encrypted.

How to populate chart data with JavaBeans collection dataSet?

I have already created a working jrxml report presenting a table populated by a dataset of a collection (List) of Java beans.
Now I would like to use that same dataset to create Chart (basic bar chart for starters). Each bean contains 4 values that I would like to show on bar chart: month, normal hours, travel hours, and overtime hours. I was hoping that each bean would generate a group of 3-bars for each month so in the end the chart would contain 12x3 bars growing from bottom to up and the name of the month would act as a label under the 3-bar groups, each group positioned next to another starting from left to right.
Unfortunately creating this chart seems much harder than I thought. At least it seems to be totally different compared to creating the table. I'm not sure if the Jasper Studio's Chart wizard is even working. At least it doesn't allow me to add any series in the chart data series dialog: if I press Add absolutely nothing happens - no dialog opens, no error message, nothing, nothing to hint me what is wrong.
Main problem is that I don't see a way to connect the dataset data to the Chart.
After trying embedding chart to my main report, I tried to add it also to a new subreport created only for to act as chart container. I passed the main report dataset as a datasource to subreport and tried to use it as main data set in subreport's chart. Still no luck with the dataset/chart connection, f.e. Still nothing happens if I press the Add-button.
Below you can see the simple beans I'm using. First one, the WorkingHoursReport is the bean I'm passing to report as JRBeanCollectionDataSource. I believe the most interesting field of that bean is the list of WorkingHours-beans. There will be always 12 items on that list: one for each month. That is the list I'm currently passing to my table element using datasource expression: new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{workingHours}).
WorkingHoursReport.java:
public class WorkingHoursReport extends CommonReport {
private int year;
private List<WorkingHours> workingHours;
}
WorkingHours.java:
public class WorkingHours {
private int month = 0;
private double hoursNormal = 0;
private double hoursTravel = 0;
private double hoursOvertime = 0;
private double hoursTotal = 0;
private double hoursTotalCumulative = 0;
}
While trying to create my first chart ever, I was naturally trying to populate the data to chart using the very same command for defining the datasource as I was already using successfully with my table:
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{workingHours}).
Unfortunately it seemed that at least the jasper studio chart creation wizard did not get any connection to the data (no dialog was opening where, according to the docs, I should have been able to select data fields to chart ).
This is how I would solve your problem considering that you are using java beans, need to sum up data and then have both series (hours) and categories (months). Don't connect it to your table datasource but create a specific datasource for it.
Create a specific chart bean
public class ChartData {
private String serie;
private String category;
private double value;
public ChartData(String serie, String category, double value) {
super();
this.serie = serie;
this.category = category;
this.value = value;
}
.... getter and setters
}
Fill the chart bean's with data
Loop your dataset (List) and fill the ChartData list, you will probably need a map to find same month and add to the hours. I will not show you this but create them statically to show an example
List<ChartData> cList = new ArrayList<ChartData>();
cList.add(new ChartData("hoursNormal","month1", 12.3)); //The use of resources or static text is beyond this example
cList.add(new ChartData("hoursTravel","month1", 3.2));
cList.add(new ChartData("hoursOvertime","month1", 1.3));
cList.add(new ChartData("hoursNormal","month2", 16.4));
cList.add(new ChartData("hoursTravel","month2", 5.2));
cList.add(new ChartData("hoursOvertime","month2", 4.1));
Pass the List as a datasource through the parameter map
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("CHART_DATASET", new JRBeanCollectionDataSource(cList));
Display the chart
Now we can display the chart in the title or summary band using a subDataset passed on the parameter $P{CHART_DATASET}
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="working_hours" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="1a12c021-57e2-4482-a273-56cbd3f78a17">
<subDataset name="chartDataSet" uuid="119b7f0e-01ef-4e2b-b628-d76f51e83768">
<field name="serie" class="java.lang.String"/>
<field name="category" class="java.lang.String"/>
<field name="value" class="java.lang.Double"/>
</subDataset>
<parameter name="CHART_DATASET" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource" isForPrompting="false"/>
<summary>
<band height="142" splitType="Stretch">
<barChart>
<chart>
<reportElement x="80" y="0" width="337" height="142" uuid="c8f4dc5d-47e7-489b-b27e-09976d90994a"/>
<chartTitle/>
<chartSubtitle/>
<chartLegend/>
</chart>
<categoryDataset>
<dataset>
<datasetRun subDataset="chartDataSet" uuid="abec2dce-b670-4e84-b71f-469d954dbcb5">
<dataSourceExpression><![CDATA[$P{CHART_DATASET}]]></dataSourceExpression>
</datasetRun>
</dataset>
<categorySeries>
<seriesExpression><![CDATA[$F{serie}]]></seriesExpression>
<categoryExpression><![CDATA[$F{category}]]></categoryExpression>
<valueExpression><![CDATA[$F{value}]]></valueExpression>
</categorySeries>
</categoryDataset>
<barPlot>
<plot/>
<itemLabel/>
<categoryAxisFormat>
<axisFormat/>
</categoryAxisFormat>
<valueAxisFormat>
<axisFormat/>
</valueAxisFormat>
</barPlot>
</barChart>
</band>
</summary>
</jasperReport>
Settings in JasperSoft Studio
Enjoy the result

Creating Subreport with list of list

I have a list of goals, each of which have another ArrayList in it. I want to have the details in the child ArrayList to be displayed using a subreport. I would like to have a subreport for each of the objects in the child ArrayList.
The issue I am facing is that, I can't seem to find a way to specify the ArrayListas data source for the subreport. When trying to create datasource, I can't select fields of the dataset on which the list is built, only the fields of the main report can be selected.
Is it possible to do this in jasper report? I am stuck with this for quite some time now.
With information given this would be the answer.
Main bean (your goals?), containing List of other bean (SubBean).
public class Bean {
private String var1;
private List<SubBean> subBeans;
public String getVar1() {
return var1;
}
public void setVar1(String var1) {
this.var1 = var1;
}
public List<SubBean> getSubBeans() {
return subBeans;
}
public void setSubBeans(List<SubBean> subBean) {
this.subBeans = subBean;
}
}
Sub bean
public class SubBean {
private String var2;
public SubBean(String var2){
this.var2 = var2;
}
public String getVar2() {
return var2;
}
public void setVar2(String var2) {
this.var2 = var2;
}
}
How to pass the SubBean List to a subreport.
You need the field subBeans in you main report.
<field name="subBeans" class="java.util.List"/>
I suggest that you pass the location of the .jasper file as a parameter (Note jasper report needs absolute path) es. in main report
<parameter name="SUBREPORT_DIR" class="java.lang.String" isForPrompting="false"/>
and in java pass it (in example sub folder "jasper" in working directory is the location of the subreport.jasper)
paramMap.put("SUBREPORT_DIR", new File("jasper").getAbsolutePath() + File.separator);
Now just call your subreport (needs to be complied into .jasper) from main report like this.
<subreport>
<reportElement x="105" y="4" width="400" height="100"/>
<dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{subBeans})]]></dataSourceExpression>
<subreportExpression class="java.lang.String"><![CDATA[$P{SUBREPORT_DIR} + "subreport.jasper"]]></subreportExpression>
</subreport>
Hence:
I'm creating a new JRDataSource for the sub report passing the List of SubBean in the current Bean
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{subBeans})
indicating the absolute location of the compiled subreport.jasper
$P{SUBREPORT_DIR} + "subreport.jasper"
so now in your subreport you can use the field: var2 of the SubBean, just define it like this in the subreport.jrxml
<field name="var2" class="java.lang.String"/>

How Vector<Vector> Receipt used as JasperReports DataSource with Java?

I have a Vector receiptOrder and I want to print that receipt to thermal printer using iReport.
I get 3 classes:
MyDataSource which implements JRDataSource
MyDataSourceProvider which implements JRDataSourceProvider
JRBasicField which implements JRField
How I can setup the JasperReports datasource with that vector?
i made this changes .
load jrxml file
JasperDesign jasperDesign = JRXmlLoader.load("c:/PrintReceipt.jrxml");
JasperReport jr = JasperCompileManager.compileReport(jasperDesign);
pass orderRows which is Vector<Vector> to MyDataSourceProvider then Create datasource
JRDataSourceProvider dsp = new MyDataSourceProvider(orderRows);
JRDataSource dataSource = dsp.create(jr);
JasperPrint jasperPrint = JasperFillManager.fillReport(jr, new HashMap(), dataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, "c:/test.pdf");
JasperViewer.viewReport(jasperPrint);
but JasperReport show me empty view and also test.pdf is empty .
any idea ...?
I fixed my problem .
The problem because of MyDataSourceProvider fields didn't match ireport.jrxml fields
After i fix that problem All thing work good .

passing value dynamically to the JASPER API to generate a Report

I am generating the Report using the Jasper Reports .
Inside the JRXML file in queryString , the query that is formed is
SELECT * FROM Address WHERE city = $P{customerId}
Now inside my servlet please tell me how can i pass this value dynamically ??
int custid = Integer.parseInt(request.getParameter("customerId")) ;
Map parameters = new HashMap();
parameters.put("customerId", custid);
Please tell me is this the correct way of passing the data ??
You can implement your own datasource (must be net.sf.jasperreports.engine.JRDataSource interface implementation).
After filling (preparing) datasource it should be passed to the JasperFillManager.fillReport(java.lang.String sourceFileName, java.util.Map parameters, JRDataSource jrDataSource) method.
The sample:
JRMapArrayDataSource dataSource = new JRMapArrayDataSource(data);
JasperPrint jasperPrint = JasperFillManager.fillReport(sourceFileName, paramsMap, dataSource);