001 /*
002 * Copyright 2011 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 */
016 package org.kuali.kfs.sys.service.impl;
017
018 import java.io.ByteArrayOutputStream;
019 import java.io.File;
020 import java.io.FileInputStream;
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.io.OutputStream;
024 import java.text.MessageFormat;
025 import java.util.Arrays;
026 import java.util.Date;
027 import java.util.List;
028 import java.util.Map;
029
030 import net.sf.jasperreports.engine.JRDataSource;
031 import net.sf.jasperreports.engine.JRException;
032 import net.sf.jasperreports.engine.JasperCompileManager;
033 import net.sf.jasperreports.engine.JasperRunManager;
034 import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
035
036 import org.apache.commons.lang.StringUtils;
037 import org.kuali.kfs.sys.KFSConstants;
038 import org.kuali.kfs.sys.KFSConstants.ReportGeneration;
039 import org.kuali.kfs.sys.service.ReportGenerationService;
040 import org.kuali.rice.kns.service.DateTimeService;
041 import org.springframework.core.io.ClassPathResource;
042 import org.springframework.ui.jasperreports.JasperReportsUtils;
043
044 /**
045 * To provide utilities that can generate reports with JasperReport
046 */
047 public 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.kfs.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(KFSConstants.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.kfs.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.kfs.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.kfs.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 }