001/* 002 * Copyright 2008 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.ole.sys.service.impl; 017 018import java.io.ByteArrayOutputStream; 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.text.MessageFormat; 025import java.util.Arrays; 026import java.util.Date; 027import java.util.List; 028import java.util.Map; 029 030import net.sf.jasperreports.engine.JRDataSource; 031import net.sf.jasperreports.engine.JRException; 032import net.sf.jasperreports.engine.JasperCompileManager; 033import net.sf.jasperreports.engine.JasperRunManager; 034import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; 035 036import org.apache.commons.lang.StringUtils; 037import org.kuali.ole.sys.OLEConstants; 038import org.kuali.ole.sys.OLEConstants.ReportGeneration; 039import org.kuali.ole.sys.service.ReportGenerationService; 040import org.kuali.rice.core.api.datetime.DateTimeService; 041import org.springframework.core.io.ClassPathResource; 042import org.springframework.ui.jasperreports.JasperReportsUtils; 043 044/** 045 * To provide utilities that can generate reports with JasperReport 046 */ 047public class ReportGenerationServiceImpl implements ReportGenerationService { 048 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ReportGenerationServiceImpl.class); 049 050 protected DateTimeService dateTimeService; 051 052 public final static String PARAMETER_NAME_SUBREPORT_DIR = ReportGeneration.PARAMETER_NAME_SUBREPORT_DIR; 053 public final static String PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME = ReportGeneration.PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME; 054 055 public final static String DESIGN_FILE_EXTENSION = ReportGeneration.DESIGN_FILE_EXTENSION; 056 public final static String JASPER_REPORT_EXTENSION = ReportGeneration.JASPER_REPORT_EXTENSION; 057 public final static String PDF_FILE_EXTENSION = ReportGeneration.PDF_FILE_EXTENSION; 058 059 public final static String SEPARATOR = "/"; 060 061 /** 062 * @see org.kuali.ole.sys.batch.service.ReportGenerationService#generateReportToPdfFile(java.util.Map, java.lang.String, java.lang.String) 063 */ 064 public void generateReportToPdfFile(Map<String, Object> reportData, String template, String reportFileName) { 065 List<String> data = Arrays.asList(OLEConstants.EMPTY_STRING); 066 JRDataSource dataSource = new JRBeanCollectionDataSource(data); 067 068 generateReportToPdfFile(reportData, dataSource, template, reportFileName); 069 } 070 071 /** 072 * The dataSource can be an instance of JRDataSource, java.util.Collection or object array. 073 * 074 * @see org.kuali.ole.sys.batch.service.ReportGenerationService#generateReportToPdfFile(java.util.Map, java.lang.Object, java.lang.String, 075 * java.lang.String) 076 */ 077 public void generateReportToPdfFile(Map<String, Object> reportData, Object dataSource, String template, String reportFileName) { 078 ClassPathResource resource = getReportTemplateClassPathResource(template); 079 if (resource == null || !resource.exists()) { 080 throw new IllegalArgumentException("Cannot find the template file: " + template); 081 } 082 083 try { 084 if (reportData != null && reportData.containsKey(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME)) { 085 Map<String, String> subReports = (Map<String, String>) reportData.get(PARAMETER_NAME_SUBREPORT_TEMPLATE_NAME); 086 String subReportDirectory = (String) reportData.get(PARAMETER_NAME_SUBREPORT_DIR); 087 compileSubReports(subReports, subReportDirectory); 088 } 089 090 String realTemplateNameWithoutExtension = removeTemplateExtension(resource); 091 String designTemplateName = realTemplateNameWithoutExtension.concat(DESIGN_FILE_EXTENSION); 092 String jasperReportName = realTemplateNameWithoutExtension.concat(JASPER_REPORT_EXTENSION); 093 compileReportTemplate(designTemplateName, jasperReportName); 094 095 JRDataSource jrDataSource = JasperReportsUtils.convertReportData(dataSource); 096 097 reportFileName = reportFileName + PDF_FILE_EXTENSION; 098 File reportDirectory = new File(StringUtils.substringBeforeLast(reportFileName, SEPARATOR)); 099 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}