View Javadoc
1   /*
2    * Copyright 2008 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.sys.service.impl;
17  
18  import java.io.ByteArrayOutputStream;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.text.MessageFormat;
25  import java.util.Arrays;
26  import java.util.Date;
27  import java.util.List;
28  import java.util.Map;
29  
30  import net.sf.jasperreports.engine.JRDataSource;
31  import net.sf.jasperreports.engine.JRException;
32  import net.sf.jasperreports.engine.JasperCompileManager;
33  import net.sf.jasperreports.engine.JasperRunManager;
34  import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
35  
36  import org.apache.commons.lang.StringUtils;
37  import org.kuali.ole.sys.OLEConstants;
38  import org.kuali.ole.sys.OLEConstants.ReportGeneration;
39  import org.kuali.ole.sys.service.ReportGenerationService;
40  import org.kuali.rice.core.api.datetime.DateTimeService;
41  import org.springframework.core.io.ClassPathResource;
42  import org.springframework.ui.jasperreports.JasperReportsUtils;
43  
44  /**
45   * To provide utilities that can generate reports with JasperReport
46   */
47  public class ReportGenerationServiceImpl implements ReportGenerationService {
48      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ReportGenerationServiceImpl.class);
49  
50      protected DateTimeService dateTimeService;
51      
52      public final static String PARAMETER_NAME_SUBREPORT_DIR = ReportGeneration.PARAMETER_NAME_SUBREPORT_DIR;
53      public final static String PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME = ReportGeneration.PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME;
54  
55      public final static String DESIGN_FILE_EXTENSION = ReportGeneration.DESIGN_FILE_EXTENSION;
56      public final static String JASPER_REPORT_EXTENSION = ReportGeneration.JASPER_REPORT_EXTENSION;
57      public final static String PDF_FILE_EXTENSION = ReportGeneration.PDF_FILE_EXTENSION;
58      
59      public final static String SEPARATOR = "/";
60  
61      /**
62       * @see org.kuali.ole.sys.batch.service.ReportGenerationService#generateReportToPdfFile(java.util.Map, java.lang.String, java.lang.String)
63       */
64      public void generateReportToPdfFile(Map<String, Object> reportData, String template, String reportFileName) {
65          List<String> data = Arrays.asList(OLEConstants.EMPTY_STRING);
66          JRDataSource dataSource = new JRBeanCollectionDataSource(data);
67  
68          generateReportToPdfFile(reportData, dataSource, template, reportFileName);
69      }
70  
71      /**
72       * The dataSource can be an instance of JRDataSource, java.util.Collection or object array.
73       * 
74       * @see org.kuali.ole.sys.batch.service.ReportGenerationService#generateReportToPdfFile(java.util.Map, java.lang.Object, java.lang.String,
75       *      java.lang.String)
76       */
77      public void generateReportToPdfFile(Map<String, Object> reportData, Object dataSource, String template, String reportFileName) {
78          ClassPathResource resource = getReportTemplateClassPathResource(template);
79          if (resource == null || !resource.exists()) {
80              throw new IllegalArgumentException("Cannot find the template file: " + template);
81          }
82  
83          try {
84              if (reportData != null && reportData.containsKey(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME)) {
85                  Map<String, String> subReports = (Map<String, String>) reportData.get(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME);
86                  String subReportDirectory = (String) reportData.get(PARAMETER_NAME_SUBREPORT_DIR);
87                  compileSubReports(subReports, subReportDirectory);
88              }
89  
90              String realTemplateNameWithoutExtension = removeTemplateExtension(resource);
91              String designTemplateName = realTemplateNameWithoutExtension.concat(DESIGN_FILE_EXTENSION);
92              String jasperReportName = realTemplateNameWithoutExtension.concat(JASPER_REPORT_EXTENSION);
93              compileReportTemplate(designTemplateName, jasperReportName);
94  
95              JRDataSource jrDataSource = JasperReportsUtils.convertReportData(dataSource);
96  
97              reportFileName = reportFileName + PDF_FILE_EXTENSION;
98              File reportDirectory = new File(StringUtils.substringBeforeLast(reportFileName, SEPARATOR));
99              if(!reportDirectory.exists()) {
100                 reportDirectory.mkdir();
101             }
102             
103             JasperRunManager.runReportToPdfFile(jasperReportName, reportFileName, reportData, jrDataSource);
104         }
105         catch (Exception e) {
106             LOG.error(e);
107             throw new RuntimeException("Fail to generate report.", e);
108         }
109     }
110 
111     /**
112      * @see org.kuali.ole.sys.batch.service.ReportGenerationService#generateReportToOutputStream(java.util.Map, java.lang.Object,
113      *      java.lang.String, java.io.ByteArrayOutputStream)
114      */
115     public void generateReportToOutputStream(Map<String, Object> reportData, Object dataSource, String template, ByteArrayOutputStream baos) {
116         ClassPathResource resource = getReportTemplateClassPathResource(template);
117         if (resource == null || !resource.exists()) {
118             throw new IllegalArgumentException("Cannot find the template file: " + template);
119         }
120 
121         try {
122             if (reportData != null && reportData.containsKey(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME)) {
123                 Map<String, String> subReports = (Map<String, String>) reportData.get(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME);
124                 String subReportDirectory = (String) reportData.get(PARAMETER_NAME_SUBREPORT_DIR);
125                 compileSubReports(subReports, subReportDirectory);
126             }
127 
128             String realTemplateNameWithoutExtension = removeTemplateExtension(resource);
129             String designTemplateName = realTemplateNameWithoutExtension.concat(DESIGN_FILE_EXTENSION);
130             String jasperReportName = realTemplateNameWithoutExtension.concat(JASPER_REPORT_EXTENSION);
131             compileReportTemplate(designTemplateName, jasperReportName);
132 
133             JRDataSource jrDataSource = JasperReportsUtils.convertReportData(dataSource);
134 
135             InputStream inputStream = new FileInputStream(jasperReportName);
136 
137             JasperRunManager.runReportToPdfStream(inputStream, (OutputStream) baos, reportData, jrDataSource);
138         }
139         catch (Exception e) {
140             LOG.error(e);
141             throw new RuntimeException("Fail to generate report.", e);
142         }
143     }
144 
145     /**
146      * @see org.kuali.ole.sys.batch.service.ReportGenerationService#buildFullFileName(java.util.Date, java.lang.String, java.lang.String,
147      *      java.lang.String)
148      */
149     public String buildFullFileName(Date runDate, String directory, String fileName, String extension) {
150         String runtimeStamp = dateTimeService.toDateTimeStringForFilename(runDate);
151         String fileNamePattern = "{0}" + SEPARATOR + "{1}_{2}{3}";
152 
153         return MessageFormat.format(fileNamePattern, directory, fileName, runtimeStamp, extension);
154     }
155 
156     /**
157      * get a class path resource that references to the given report template
158      * 
159      * @param reportTemplateName the given report template name with its full-qualified package name. It may not include extension.
160      *        If an extension is included in the name, it should be prefixed ".jasper" or '.jrxml".
161      * @return a class path resource that references to the given report template
162      */
163     protected ClassPathResource getReportTemplateClassPathResource(String reportTemplateName) {
164         if (reportTemplateName.endsWith(DESIGN_FILE_EXTENSION) || reportTemplateName.endsWith(JASPER_REPORT_EXTENSION)) {
165             return new ClassPathResource(reportTemplateName);
166         }
167 
168         String jasperReport = reportTemplateName.concat(JASPER_REPORT_EXTENSION);
169         ClassPathResource resource = new ClassPathResource(jasperReport);
170         if (resource.exists()) {
171             return resource;
172         }
173 
174         String designTemplate = reportTemplateName.concat(DESIGN_FILE_EXTENSION);
175         resource = new ClassPathResource(designTemplate);
176         return resource;
177     }
178 
179     /**
180      * complie the report template xml file into a Jasper report file if the compiled file does not exist or is out of update
181      * 
182      * @param designTemplate the full name of the report template xml file
183      * @param jasperReport the full name of the compiled report file
184      */
185     protected void compileReportTemplate(String designTemplate, String jasperReport) throws JRException {
186         File jasperFile = new File(jasperReport);
187         File designFile = new File(designTemplate);
188 
189         if (!jasperFile.exists() && !designFile.exists()) {
190             throw new RuntimeException("Both the design template file and jasper report file don't exist: (" + designTemplate + ", " + jasperReport + ")");
191         }
192 
193         if (!jasperFile.exists() && designFile.exists()) {
194             JasperCompileManager.compileReportToFile(designTemplate, jasperReport);
195         }
196         else if (jasperFile.exists() && designFile.exists()) {
197             if (jasperFile.lastModified() < designFile.lastModified()) {
198                 JasperCompileManager.compileReportToFile(designTemplate, jasperReport);
199             }
200         }
201     }
202 
203     /**
204      * compile the given sub reports
205      * 
206      * @param subReports the sub report Map that hold the sub report templete names indexed with keys
207      * @param subReportDirectory the directory where sub report templates are located
208      */
209     protected void compileSubReports(Map<String, String> subReports, String subReportDirectory) throws Exception {
210         for (Map.Entry<String, String> entry: subReports.entrySet()) {
211             ClassPathResource resource = getReportTemplateClassPathResource(subReportDirectory + entry.getValue());
212             String realTemplateNameWithoutExtension = removeTemplateExtension(resource);
213 
214             String designTemplateName = realTemplateNameWithoutExtension.concat(DESIGN_FILE_EXTENSION);
215             String jasperReportName = realTemplateNameWithoutExtension.concat(JASPER_REPORT_EXTENSION);
216 
217             compileReportTemplate(designTemplateName, jasperReportName);
218         }
219     }
220 
221     /**
222      * remove the file extension of the given template if any
223      * 
224      * @param template the given template
225      * @return the template without file extension
226      */
227     protected String removeTemplateExtension(ClassPathResource template) throws IOException {
228         String realTemplateName = template.getFile().getAbsolutePath();
229 
230         int lastIndex = realTemplateName.lastIndexOf(".");
231         String realTemplateNameWithoutExtension = lastIndex > 0 ? realTemplateName.substring(0, lastIndex) : realTemplateName;
232 
233         return realTemplateNameWithoutExtension;
234     }
235 
236     /**
237      * Sets the DateTimeService
238      * 
239      * @param dateTimeService The dateTimeService to set.
240      */
241     public void setDateTimeService(DateTimeService dateTimeService) {
242         this.dateTimeService = dateTimeService;
243     }
244 }