Sunday, November 13, 2016

Programmatically created report with custom datasource in JasperServer

Sometimes there is need to create report programmatically. So let's do this in JasperServer.
Create empty jrxml file and save as dynamic.jrxml
<?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="dynamic" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="aa4ecc63-a8e6-409f-a877-1caf24b98220"/>
Create ant build.xml file
<project name="dynamic" default="deploy" basedir=".">
    <!-- Points to where jasperserver is installed on tomcat or another webapp server.
         Used to find JARs for compiling as well as deploying
    -->
    <property name="webAppDir" value="c:\\Jaspersoft\\jasperreports-server-cp-6.3.0\\apache-tomcat\\webapps\\jasperserver"/>

    <target name="clean">
        <delete dir="classes"/>
    </target>
    <target name="compile">
        <mkdir dir="classes"/>
        <javac srcdir="src" destdir="classes" debug="true">
            <classpath>
                <fileset dir="${webAppDir}/WEB-INF/lib">
                    <include name="*.jar"/>
                </fileset>
                <fileset dir="webapp/WEB-INF/lib">
                    <include name="*.jar"/>
                </fileset>
            </classpath>
        </javac>
    </target>
 <target name="deploy" depends="compile">
        <echo message="webapp src..."/>
  <copy todir="${webAppDir}">
   <fileset dir="webapp"/>
  </copy>
        <!-- copy classes dir -->
        <echo message="classes..."/>
  <copy todir="${webAppDir}/WEB-INF/classes">
   <fileset dir="classes"/>
  </copy>
 </target>
</project>
Create DynamicEngineServiceImpl.java file

package pl.aszywala.dynamic;

import java.awt.Color;
import java.io.InputStream;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.EngineServiceImpl;
import com.jaspersoft.jasperserver.api.metadata.common.domain.FileResource;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignField;
import net.sf.jasperreports.engine.design.JRDesignFrame;
import net.sf.jasperreports.engine.design.JRDesignGroup;
import net.sf.jasperreports.engine.design.JRDesignLine;
import net.sf.jasperreports.engine.design.JRDesignParameter;
import net.sf.jasperreports.engine.design.JRDesignSection;
import net.sf.jasperreports.engine.design.JRDesignStaticText;
import net.sf.jasperreports.engine.design.JRDesignStyle;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JRDesignVariable;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.type.CalculationEnum;
import net.sf.jasperreports.engine.type.HorizontalAlignEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.PositionTypeEnum;
import net.sf.jasperreports.engine.type.ResetTypeEnum;
import net.sf.jasperreports.repo.JasperDesignCache;

public class DynamicEngineServiceImpl extends EngineServiceImpl {

 protected static final Log log = LogFactory.getLog(DynamicEngineServiceImpl.class);

 protected JasperReport getJasperReport(ExecutionContext context, JasperDesignCache cache, FileResource reportRes,
   String location, boolean inMemoryUnit) {
  if ("/reports/dynamic".equals(location)) {
   return getDynamicReport(context, reportRes);
  } else {
   return super.getJasperReport(context, cache, reportRes, location, inMemoryUnit);
  }
 }

 private JasperReport getDynamicReport(ExecutionContext context, FileResource reportRes) {
  try {
   return compileReport(getJasperDesign(loadReportDesign(getFileResourceDataStream(context, reportRes))));
  } catch (JRException e) {
   log.error(e, e);
   throw new JSExceptionWrapper(e);
  }
 }

 private static JasperDesign getJasperDesign(JasperDesign jasperDesign) throws JRException {
  // JasperDesign
  jasperDesign.setName("NoXmlDesignReport");
  jasperDesign.setPageWidth(595);
  jasperDesign.setPageHeight(842);
  jasperDesign.setColumnWidth(515);
  jasperDesign.setColumnSpacing(0);
  jasperDesign.setLeftMargin(40);
  jasperDesign.setRightMargin(40);
  jasperDesign.setTopMargin(50);
  jasperDesign.setBottomMargin(50);

  // Fonts
  JRDesignStyle normalStyle = new JRDesignStyle();
  normalStyle.setName("Sans_Normal");
  normalStyle
    .setForecolor(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
  normalStyle.setDefault(true);
  normalStyle.setFontName("DejaVu Sans");
  normalStyle.setPdfFontName("Helvetica");
  normalStyle.setPdfEncoding("Cp1252");
  normalStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(normalStyle);

  JRDesignStyle boldStyle = new JRDesignStyle();
  boldStyle.setName("Sans_Bold");
  boldStyle.setFontName("DejaVu Sans");
  boldStyle.setBold(true);
  boldStyle.setPdfFontName("Helvetica-Bold");
  boldStyle.setPdfEncoding("Cp1252");
  boldStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(boldStyle);

  JRDesignStyle italicStyle = new JRDesignStyle();
  italicStyle.setName("Sans_Italic");
  italicStyle.setFontName("DejaVu Sans");
  italicStyle.setItalic(true);
  italicStyle.setPdfFontName("Helvetica-Oblique");
  italicStyle.setPdfEncoding("Cp1252");
  italicStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(italicStyle);

  // Parameters
  JRDesignParameter parameter = new JRDesignParameter();
  parameter.setName("ReportTitle");
  parameter.setValueClass(java.lang.String.class);
  jasperDesign.addParameter(parameter);

  parameter = new JRDesignParameter();
  parameter.setName("OrderByClause");
  parameter.setValueClass(java.lang.String.class);
  jasperDesign.addParameter(parameter);

  JRDesignField field = new JRDesignField();
  field.setName("FirstName");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("LastName");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("Street");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("City");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  // Variables
  JRDesignVariable variable = new JRDesignVariable();
  variable.setName("CityNumber");
  variable.setValueClass(java.lang.Integer.class);
  variable.setResetType(ResetTypeEnum.GROUP);
  JRDesignGroup group = new JRDesignGroup();
  group.setName("CityGroup");
  variable.setResetGroup(group);
  variable.setCalculation(CalculationEnum.SYSTEM);
  JRDesignExpression expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("($V{CityNumber} != null)?(new Integer($V{CityNumber}.intValue() + 1)):(new Integer(1))");
  variable.setInitialValueExpression(expression);
  jasperDesign.addVariable(variable);

  variable = new JRDesignVariable();
  variable.setName("AllCities");
  variable.setValueClass(java.lang.String.class);
  variable.setResetType(ResetTypeEnum.REPORT);
  variable.setCalculation(CalculationEnum.SYSTEM);
  jasperDesign.addVariable(variable);

  // Groups
  group.setMinHeightToStartNewPage(60);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{City}");
  group.setExpression(expression);

  JRDesignBand band = new JRDesignBand();
  band.setHeight(20);
  JRDesignTextField textField = new JRDesignTextField();
  textField.setX(0);
  textField.setY(4);
  textField.setWidth(515);
  textField.setHeight(15);
  textField.setBackcolor(new Color(0xC0, 0xC0, 0xC0));
  textField.setMode(ModeEnum.OPAQUE);
  textField.setHorizontalAlignment(HorizontalAlignEnum.LEFT);
  textField.setStyle(boldStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("\"  \" + String.valueOf($V{CityNumber}) + \". \" + String.valueOf($F{City})");
  textField.setExpression(expression);
  band.addElement(textField);
  JRDesignLine line = new JRDesignLine();
  line.setX(0);
  line.setY(19);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  ((JRDesignSection) group.getGroupHeaderSection()).addBand(band);

  band = new JRDesignBand();
  band.setHeight(20);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(-1);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  JRDesignStaticText staticText = new JRDesignStaticText();
  staticText.setX(400);
  staticText.setY(0);
  staticText.setWidth(60);
  staticText.setHeight(15);
  staticText.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  staticText.setStyle(boldStyle);
  staticText.setText("Count : ");
  band.addElement(staticText);
  textField = new JRDesignTextField();
  textField.setX(460);
  textField.setY(0);
  textField.setWidth(30);
  textField.setHeight(15);
  textField.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  textField.setStyle(boldStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("$V{CityGroup_COUNT}");
  textField.setExpression(expression);
  band.addElement(textField);
  ((JRDesignSection) group.getGroupFooterSection()).addBand(band);

  jasperDesign.addGroup(group);

  // Title
  band = new JRDesignBand();
  band.setHeight(50);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(0);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  textField = new JRDesignTextField();
  textField.setBlankWhenNull(true);
  textField.setX(0);
  textField.setY(10);
  textField.setWidth(515);
  textField.setHeight(30);
  textField.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
  textField.setStyle(normalStyle);
  textField.setFontSize(22);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$P{ReportTitle}");
  textField.setExpression(expression);
  band.addElement(textField);
  jasperDesign.setTitle(band);

  // Page header
  band = new JRDesignBand();
  band.setHeight(20);
  JRDesignFrame frame = new JRDesignFrame();
  frame.setX(0);
  frame.setY(5);
  frame.setWidth(515);
  frame.setHeight(15);
  frame.setForecolor(new Color(0x33, 0x33, 0x33));
  frame.setBackcolor(new Color(0x33, 0x33, 0x33));
  frame.setMode(ModeEnum.OPAQUE);
  band.addElement(frame);
  staticText = new JRDesignStaticText();
  staticText.setX(0);
  staticText.setY(0);
  staticText.setWidth(55);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
  staticText.setStyle(boldStyle);
  staticText.setText("First Name");
  frame.addElement(staticText);
  staticText = new JRDesignStaticText();
  staticText.setX(55);
  staticText.setY(0);
  staticText.setWidth(205);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setStyle(boldStyle);
  staticText.setText("Last Name");
  frame.addElement(staticText);
  staticText = new JRDesignStaticText();
  staticText.setX(260);
  staticText.setY(0);
  staticText.setWidth(255);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setStyle(boldStyle);
  staticText.setText("Street");
  frame.addElement(staticText);
  jasperDesign.setPageHeader(band);

  // Column header
  band = new JRDesignBand();
  jasperDesign.setColumnHeader(band);

  // Detail
  band = new JRDesignBand();
  band.setHeight(20);
  textField = new JRDesignTextField();
  textField.setX(0);
  textField.setY(4);
  textField.setWidth(50);
  textField.setHeight(15);
  textField.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("$F{FirstName}");
  textField.setExpression(expression);
  band.addElement(textField);
  textField = new JRDesignTextField();
  textField.setStretchWithOverflow(true);
  textField.setX(55);
  textField.setY(4);
  textField.setWidth(200);
  textField.setHeight(15);
  textField.setPositionType(PositionTypeEnum.FLOAT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{LastName}");
  textField.setExpression(expression);
  band.addElement(textField);
  textField = new JRDesignTextField();
  textField.setStretchWithOverflow(true);
  textField.setX(260);
  textField.setY(4);
  textField.setWidth(255);
  textField.setHeight(15);
  textField.setPositionType(PositionTypeEnum.FLOAT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{Street}");
  textField.setExpression(expression);
  band.addElement(textField);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(19);
  line.setWidth(515);
  line.setHeight(0);
  line.setForecolor(new Color(0x80, 0x80, 0x80));
  line.setPositionType(PositionTypeEnum.FLOAT);
  band.addElement(line);
  ((JRDesignSection) jasperDesign.getDetailSection()).addBand(band);

  // Column footer
  band = new JRDesignBand();
  jasperDesign.setColumnFooter(band);

  // Page footer
  band = new JRDesignBand();
  jasperDesign.setPageFooter(band);

  // Summary
  band = new JRDesignBand();
  jasperDesign.setSummary(band);

  return jasperDesign;
 }
}
When report location equals to /reports/dynamic then report is generated programmatically. Every time when report is generated font is changed to random color so you can see that report is changing. </ br> Open js.spring.properties file. It is located in WEB-INF folder of the JasperServer web application directory in my case is c:\Jaspersoft\jasperreports-server-cp-6.3.0\apache-tomcat\webapps\jasperserver\WEB-INF\ and change bean.class.engineService to:
bean.class.engineService=pl.aszywala.dynamic.DynamicEngineServiceImpl
Let's create custom datasource for the report. Create DynamicDataSource class
package pl.aszywala.dynamic;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;

public class DynamicDataSource implements JRDataSource {

 private Object[][] data = { { "Katowice", "Jan", "Kowalski", "Staszica 1" },
   { "Katowice", "Piotr", "Nowak", "Warszawska 11" },
   { "New York", "Andrew", "May", "172 Seventh Av." },
   { "New York", "Sylvia", "Ott", "361 College Av." },
   { "New York", "Bill", "King", "546 College Av." },
   { "Paris", "Sylvia", "Steel", "269 College Av." },
   { "Paris", "Sylvia", "Fuller", "158 - 20th Ave." },
   { "Paris", "Laura", "Miller", "294 Seventh Av." },
   { "San Francisco","Robert", "White", "549 Seventh Av." },
   { "San Francisco", "James", "Peterson", "231 Upland Pl." } };

 private int index = -1;

 public DynamicDataSource() {
 }

 public boolean next() throws JRException {
  return (++index < data.length);
 }

 public Object getFieldValue(JRField field) throws JRException {
  Object value = null;

  String fieldName = field.getName();

  if ("City".equals(fieldName)) {
   value = data[index][0];
  } else if ("FirstName".equals(fieldName)) {
   value = data[index][1];
  } else if ("LastName".equals(fieldName)) {
   value = data[index][2];
  } else if ("Street".equals(fieldName)) {
   value = data[index][3];
  }

  return value;
 }

}
Create DynamicDataSourceService class
package pl.aszywala.dynamic;

import java.util.Map;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRParameter;

import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService;

public class DynamicDataSourceService implements ReportDataSourceService {

 JRDataSource ds;
 private RepositoryService repository;
 private Map propertyMap;
 
 public Map getPropertyMap() {
  return propertyMap;
 }

 public void setPropertyMap(Map propertyMap) {
  this.propertyMap = propertyMap;
 }

 public DynamicDataSourceService() {
  this.ds = new DynamicDataSource();
 }
 
 public DynamicDataSourceService(JRDataSource ds) {
  this.ds = ds;
 }
 
 public void closeConnection() {
  // Do nothing
 }

 public void setReportParameterValues(Map parameterValues) {
  parameterValues.put(JRParameter.REPORT_DATA_SOURCE, ds);
 }

 public RepositoryService getRepository() {
  return repository;
 }

 public void setRepository(RepositoryService repository) {
  this.repository = repository;
 }

}
In WEB-INF/bundles directory create dynamic.properties file
dynamicDataSource.name=Dynamic Data Source
SEARCH_dynamicDataSource=Dynamic Data Source
resource.dynamicDataSource.label=Dynamic Data Source
The last thing is to create applicationContext-dynamic.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
       
 <bean id="dynamicDataSource" class="com.jaspersoft.jasperserver.api.engine.jasperreports.util.CustomDataSourceDefinition">
  <property name="factory" ref="customDataSourceServiceFactory"/>
  <property name="name" value="dynamicDataSource"/>
  <property name="serviceClassName" value="pl.aszywala.dynamic.DynamicDataSourceService"/> 
  <property name="propertyDefinitions">
   <list/>    
  </property>
  <property name="queryExecuterMap">
   <map/>
  </property> 
 </bean>

    <bean class="com.jaspersoft.jasperserver.api.common.util.spring.GenericBeanUpdater">
        <property name="definition" ref="addMessageCatalog"/>
        <property name="value">
            <list>
                <value>WEB-INF/bundles/dynamic</value>
            </list>
        </property>
        <property name="valueType" value="stringList"/>
    </bean>
</beans>
Run ant command in directory where your build.xml file is placed. It should build the project and copy files to the JasperServer webapp directory. Restart JasperServer and login to the dashboard.
Add new datasource
Add new report
And connect it to the dynamicDS datasource
Run the raport
Source code for the blog post is available on the GitHub

No comments:

Post a Comment