1 16 17 package org.springframework.web.servlet.view.jasperreports; 18 19 import java.io.IOException ; 20 import java.lang.reflect.Field ; 21 import java.sql.Connection ; 22 import java.sql.SQLException ; 23 import java.util.Collection ; 24 import java.util.Enumeration ; 25 import java.util.HashMap ; 26 import java.util.Iterator ; 27 import java.util.Locale ; 28 import java.util.Map ; 29 import java.util.Properties ; 30 import java.util.ResourceBundle ; 31 32 import javax.servlet.http.HttpServletRequest ; 33 import javax.servlet.http.HttpServletResponse ; 34 import javax.sql.DataSource ; 35 36 import net.sf.jasperreports.engine.JRDataSource; 37 import net.sf.jasperreports.engine.JRDataSourceProvider; 38 import net.sf.jasperreports.engine.JRException; 39 import net.sf.jasperreports.engine.JRExporterParameter; 40 import net.sf.jasperreports.engine.JRParameter; 41 import net.sf.jasperreports.engine.JasperFillManager; 42 import net.sf.jasperreports.engine.JasperPrint; 43 import net.sf.jasperreports.engine.JasperReport; 44 import net.sf.jasperreports.engine.design.JRCompiler; 45 import net.sf.jasperreports.engine.design.JRDefaultCompiler; 46 import net.sf.jasperreports.engine.design.JasperDesign; 47 import net.sf.jasperreports.engine.util.JRLoader; 48 import net.sf.jasperreports.engine.xml.JRXmlLoader; 49 50 import org.springframework.context.ApplicationContextException; 51 import org.springframework.context.support.MessageSourceResourceBundle; 52 import org.springframework.core.io.Resource; 53 import org.springframework.ui.jasperreports.JasperReportsUtils; 54 import org.springframework.util.ClassUtils; 55 import org.springframework.util.CollectionUtils; 56 import org.springframework.web.servlet.support.RequestContextUtils; 57 import org.springframework.web.servlet.view.AbstractUrlBasedView; 58 59 117 public abstract class AbstractJasperReportsView extends AbstractUrlBasedView { 118 119 122 protected static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; 123 124 127 protected static final String CONTENT_DISPOSITION_INLINE = "inline"; 128 129 130 133 private String reportDataKey; 134 135 139 private Properties subReportUrls; 140 141 146 private String [] subReportDataKeys; 147 148 151 private Properties headers; 152 153 157 private Map exporterParameters = new HashMap (); 158 159 162 private Map convertedExporterParameters; 163 164 167 private DataSource jdbcDataSource; 168 169 172 private JRCompiler reportCompiler = JRDefaultCompiler.getInstance(); 173 174 177 private JasperReport report; 178 179 182 private Map subReports; 183 184 185 201 public void setReportDataKey(String reportDataKey) { 202 this.reportDataKey = reportDataKey; 203 } 204 205 214 public void setSubReportUrls(Properties subReports) { 215 this.subReportUrls = subReports; 216 } 217 218 238 public void setSubReportDataKeys(String [] subReportDataKeys) { 239 this.subReportDataKeys = subReportDataKeys; 240 } 241 242 246 public void setHeaders(Properties headers) { 247 this.headers = headers; 248 } 249 250 257 public void setExporterParameters(Map parameters) { 258 this.exporterParameters = parameters; 264 } 265 266 269 public Map getExporterParameters() { 270 return this.exporterParameters; 271 } 272 273 276 protected Map getConvertedExporterParameters() { 277 return this.convertedExporterParameters; 278 } 279 280 284 public void setJdbcDataSource(DataSource jdbcDataSource) { 285 this.jdbcDataSource = jdbcDataSource; 286 } 287 288 291 protected DataSource getJdbcDataSource() { 292 return this.jdbcDataSource; 293 } 294 295 302 public void setReportCompiler(JRCompiler reportCompiler) { 303 this.reportCompiler = (reportCompiler != null ? reportCompiler : JRDefaultCompiler.getInstance()); 304 } 305 306 309 protected JRCompiler getReportCompiler() { 310 return this.reportCompiler; 311 } 312 313 314 321 protected final void initApplicationContext() throws ApplicationContextException { 322 Resource mainReport = getApplicationContext().getResource(getUrl()); 323 this.report = loadReport(mainReport); 324 325 if (this.subReportUrls != null) { 327 if (this.subReportDataKeys != null && this.subReportDataKeys.length > 0 && this.reportDataKey == null) { 328 throw new ApplicationContextException( 329 "'reportDataKey' for main report is required when specifying a value for 'subReportDataKeys'"); 330 } 331 this.subReports = new HashMap (this.subReportUrls.size()); 332 for (Enumeration urls = this.subReportUrls.propertyNames(); urls.hasMoreElements();) { 333 String key = (String ) urls.nextElement(); 334 String path = this.subReportUrls.getProperty(key); 335 Resource resource = getApplicationContext().getResource(path); 336 this.subReports.put(key, loadReport(resource)); 337 } 338 } 339 340 convertExporterParameters(); 342 343 if (this.headers == null) { 344 this.headers = new Properties (); 345 } 346 if (!this.headers.containsKey(HEADER_CONTENT_DISPOSITION)) { 347 this.headers.setProperty(HEADER_CONTENT_DISPOSITION, CONTENT_DISPOSITION_INLINE); 348 } 349 350 onInit(); 351 } 352 353 359 protected void onInit() { 360 } 361 362 369 protected final void convertExporterParameters() { 370 if (this.exporterParameters != null && !this.exporterParameters.isEmpty()) { 371 this.convertedExporterParameters = new HashMap (this.exporterParameters.size()); 372 for (Iterator it = this.exporterParameters.entrySet().iterator(); it.hasNext();) { 373 Map.Entry entry = (Map.Entry ) it.next(); 374 JRExporterParameter exporterParameter = getExporterParameter(entry.getKey()); 375 this.convertedExporterParameters.put(exporterParameter, convertParameterValue(exporterParameter, entry.getValue())); 376 } 377 } 378 } 379 380 388 protected Object convertParameterValue(JRExporterParameter parameter, Object value) { 389 if (value instanceof String ) { 390 String str = (String ) value; 391 if ("true".equals(str)) { 392 return Boolean.TRUE; 393 } 394 else if ("false".equals(str)) { 395 return Boolean.FALSE; 396 } 397 else if (str.length() > 0 && Character.isDigit(str.charAt(0))) { 398 try { 400 return new Integer (str); 401 } 402 catch (NumberFormatException ex) { 403 return str; 405 } 406 } 407 } 408 return value; 409 } 410 411 418 protected JRExporterParameter getExporterParameter(Object parameter) { 419 if (parameter instanceof JRExporterParameter) { 420 return (JRExporterParameter) parameter; 421 } 422 if (parameter instanceof String ) { 423 return convertToExporterParameter((String ) parameter); 424 } 425 throw new IllegalArgumentException ( 426 "Parameter [" + parameter + "] is invalid type. Should be either String or JRExporterParameter."); 427 } 428 429 437 protected JRExporterParameter convertToExporterParameter(String fqFieldName) { 438 int index = fqFieldName.lastIndexOf('.'); 439 if (index == -1 || index == fqFieldName.length()) { 440 throw new IllegalArgumentException ( 441 "Parameter name [" + fqFieldName + "] is not a valid static field. " + 442 "The parameter name must map to a static field such as " + 443 "[net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI]"); 444 } 445 String className = fqFieldName.substring(0, index); 446 String fieldName = fqFieldName.substring(index + 1); 447 448 try { 449 Class cls = ClassUtils.forName(className); 450 Field field = cls.getField(fieldName); 451 452 if (JRExporterParameter.class.isAssignableFrom(field.getType())) { 453 try { 454 return (JRExporterParameter) field.get(null); 455 } 456 catch (IllegalAccessException ex) { 457 throw new IllegalArgumentException ( 458 "Unable to access field [" + fieldName + "] of class [" + className + "]. " + 459 "Check that it is static and accessible."); 460 } 461 } 462 else { 463 throw new IllegalArgumentException ("Field [" + fieldName + "] on class [" + className + 464 "] is not assignable from JRExporterParameter - check the type of this field."); 465 } 466 } 467 catch (ClassNotFoundException ex) { 468 throw new IllegalArgumentException ( 469 "Class [" + className + "] in key [" + fqFieldName + "] could not be found."); 470 } 471 catch (NoSuchFieldException ex) { 472 throw new IllegalArgumentException ("Field [" + fieldName + "] in key [" + fqFieldName + 473 "] could not be found on class [" + className + "]."); 474 } 475 } 476 477 484 private JasperReport loadReport(Resource resource) throws ApplicationContextException { 485 try { 486 String fileName = resource.getFilename(); 487 if (fileName.endsWith(".jasper")) { 488 if (logger.isInfoEnabled()) { 490 logger.info("Loading pre-compiled Jasper Report from " + resource); 491 } 492 return (JasperReport) JRLoader.loadObject(resource.getInputStream()); 493 } 494 else if (fileName.endsWith(".jrxml")) { 495 if (logger.isInfoEnabled()) { 497 logger.info("Compiling Jasper Report loaded from " + resource); 498 } 499 JasperDesign design = JRXmlLoader.load(resource.getInputStream()); 500 return getReportCompiler().compileReport(design); 501 } 502 else { 503 throw new IllegalArgumentException ( 504 "Report URL [" + getUrl() + "] must end in either .jasper or .jrxml"); 505 } 506 } 507 catch (IOException ex) { 508 throw new ApplicationContextException( 509 "Could not load JasperReports report for URL [" + getUrl() + "]", ex); 510 } 511 catch (JRException ex) { 512 throw new ApplicationContextException( 513 "Could not parse JasperReports report for URL [" + getUrl() + "]", ex); 514 } 515 } 516 517 518 526 protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) 527 throws Exception { 528 529 if (this.subReports != null) { 530 model.putAll(this.subReports); 532 533 if (this.subReportDataKeys != null) { 535 for (int i = 0; i < this.subReportDataKeys.length; i++) { 536 String key = this.subReportDataKeys[i]; 537 model.put(key, convertReportData(model.get(key))); 538 } 539 } 540 } 541 542 exposeLocalizationContext(model, request); 544 545 JasperPrint filledReport = fillReport(model); 547 postProcessReport(filledReport, model); 548 549 response.reset(); 551 populateHeaders(response); 552 renderReport(filledReport, model, response); 553 } 554 555 569 protected void exposeLocalizationContext(Map model, HttpServletRequest request) { 570 Locale locale = RequestContextUtils.getLocale(request); 571 model.put(JRParameter.REPORT_LOCALE, locale); 572 if (this.report.getResourceBundle() == null) { 573 ResourceBundle bundle = new MessageSourceResourceBundle(getApplicationContext(), locale); 574 model.put(JRParameter.REPORT_RESOURCE_BUNDLE, bundle); 575 } 576 } 577 578 595 protected JasperPrint fillReport(Map model) throws IllegalArgumentException , SQLException , JRException { 596 JRDataSource jrDataSource = getReportData(model); 598 599 if (jrDataSource != null) { 600 if (logger.isDebugEnabled()) { 602 logger.debug("Filling report with JRDataSource [" + jrDataSource + "]."); 603 } 604 return JasperFillManager.fillReport(this.report, model, jrDataSource); 605 } 606 607 else { 608 if (this.jdbcDataSource == null) { 609 this.jdbcDataSource = (DataSource ) CollectionUtils.findValueOfType(model.values(), DataSource .class); 610 if (this.jdbcDataSource == null) { 611 throw new IllegalArgumentException ( 612 "No report data source found in model, " + 613 "and no [javax.sql.DataSource] specified in configuration or in model"); 614 } 615 } 616 617 if (logger.isDebugEnabled()) { 619 logger.debug("Filling report with JDBC DataSource [" + this.jdbcDataSource + "]."); 620 } 621 Connection con = this.jdbcDataSource.getConnection(); 622 try { 623 return JasperFillManager.fillReport(this.report, model, con); 624 } 625 finally { 626 try { 627 con.close(); 628 } 629 catch (SQLException ex) { 630 logger.warn("Could not close JDBC Connection", ex); 631 } 632 } 633 } 634 } 635 636 640 private void populateHeaders(HttpServletResponse response) { 641 for (Enumeration en = this.headers.propertyNames(); en.hasMoreElements();) { 643 String key = (String ) en.nextElement(); 644 response.addHeader(key, this.headers.getProperty(key)); 645 } 646 } 647 648 661 protected JRDataSource getReportData(Map model) { 662 if (this.reportDataKey != null) { 664 Object value = model.get(this.reportDataKey); 665 return convertReportData(value); 666 } 667 668 Object value = CollectionUtils.findValueOfType(model.values(), getReportDataTypes()); 670 671 if (value != null) { 672 return convertReportData(value); 673 } 674 675 return null; 676 } 677 678 697 protected JRDataSource convertReportData(Object value) throws IllegalArgumentException { 698 if (value instanceof JRDataSourceProvider) { 699 try { 700 return ((JRDataSourceProvider) value).create(this.report); 701 } 702 catch (JRException ex) { 703 throw new IllegalArgumentException ("Supplied JRDataSourceProvider is invalid: " + ex); 704 } 705 } 706 else { 707 return JasperReportsUtils.convertReportData(value); 708 } 709 } 710 711 721 protected Class [] getReportDataTypes() { 722 return new Class [] {JRDataSource.class, JRDataSourceProvider.class, Collection .class, Object [].class}; 723 } 724 725 730 protected JasperReport getReport() { 731 return this.report; 732 } 733 734 735 743 protected void postProcessReport(JasperPrint populatedReport, Map model) throws Exception { 744 } 745 746 764 protected abstract void renderReport(JasperPrint populatedReport, Map model, HttpServletResponse response) 765 throws Exception ; 766 767 } 768 | Popular Tags |