Source Code Cross Referenced for AbstractJasperReportsView.java in  » J2EE » spring-framework-2.0.6 » org » springframework » web » servlet » view » jasperreports » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.web.servlet.view.jasperreports 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright 2002-2006 the original author or authors.
003:         *
004:         * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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:
017:        package org.springframework.web.servlet.view.jasperreports;
018:
019:        import java.io.IOException;
020:        import java.lang.reflect.Field;
021:        import java.sql.Connection;
022:        import java.sql.SQLException;
023:        import java.util.Collection;
024:        import java.util.Enumeration;
025:        import java.util.HashMap;
026:        import java.util.Iterator;
027:        import java.util.Locale;
028:        import java.util.Map;
029:        import java.util.Properties;
030:        import java.util.ResourceBundle;
031:
032:        import javax.servlet.http.HttpServletRequest;
033:        import javax.servlet.http.HttpServletResponse;
034:        import javax.sql.DataSource;
035:
036:        import net.sf.jasperreports.engine.JRDataSource;
037:        import net.sf.jasperreports.engine.JRDataSourceProvider;
038:        import net.sf.jasperreports.engine.JRException;
039:        import net.sf.jasperreports.engine.JRExporterParameter;
040:        import net.sf.jasperreports.engine.JRParameter;
041:        import net.sf.jasperreports.engine.JasperFillManager;
042:        import net.sf.jasperreports.engine.JasperPrint;
043:        import net.sf.jasperreports.engine.JasperReport;
044:        import net.sf.jasperreports.engine.design.JRCompiler;
045:        import net.sf.jasperreports.engine.design.JRDefaultCompiler;
046:        import net.sf.jasperreports.engine.design.JasperDesign;
047:        import net.sf.jasperreports.engine.util.JRLoader;
048:        import net.sf.jasperreports.engine.xml.JRXmlLoader;
049:
050:        import org.springframework.context.ApplicationContextException;
051:        import org.springframework.context.support.MessageSourceResourceBundle;
052:        import org.springframework.core.io.Resource;
053:        import org.springframework.ui.jasperreports.JasperReportsUtils;
054:        import org.springframework.util.ClassUtils;
055:        import org.springframework.util.CollectionUtils;
056:        import org.springframework.web.servlet.support.RequestContextUtils;
057:        import org.springframework.web.servlet.view.AbstractUrlBasedView;
058:
059:        /**
060:         * Base class for all JasperReports views. Applies on-the-fly compilation
061:         * of report designs as required and coordinates the rendering process.
062:         * The resource path of the main report needs to be specified as <code>url</code>.
063:         *
064:         * <p>This class is responsible for getting report data from the model that has
065:         * been provided to the view. The default implementation checks for a model object
066:         * under the specified <code>reportDataKey</code> first, then falls back to looking
067:         * for a value of type <code>JRDataSource</code>, <code>java.util.Collection</code>,
068:         * object array (in that order).
069:         *
070:         * <p>If no <code>JRDataSource</code> can be found in the model, then reports will
071:         * be filled using the configured <code>javax.sql.DataSource</code> if any. If neither
072:         * a <code>JRDataSource</code> or <code>javax.sql.DataSource</code> is available then
073:         * an <code>IllegalArgumentException</code> is raised.
074:         *
075:         * <p>Provides support for sub-reports through the <code>subReportUrls</code> and
076:         * <code>subReportDataKeys</code> properties.
077:         *
078:         * <p>When using sub-reports, the master report should be configured using the
079:         * <code>url</code> property and the sub-reports files should be configured using
080:         * the <code>subReportUrls</code> property. Each entry in the <code>subReportUrls</code>
081:         * Map corresponds to an individual sub-report. The key of an entry must match up
082:         * to a sub-report parameter in your report file of type
083:         * <code>net.sf.jasperreports.engine.JasperReport</code>,
084:         * and the value of an entry must be the URL for the sub-report file.
085:         *
086:         * <p>For sub-reports that require an instance of <code>JRDataSource</code>, that is,
087:         * they don't have a hard-coded query for data retrieval, you can include the
088:         * appropriate data in your model as would with the data source for the parent report.
089:         * However, you must provide a List of parameter names that need to be converted to
090:         * <code>JRDataSource</code> instances for the sub-report via the
091:         * <code>subReportDataKeys</code> property. When using <code>JRDataSource</code>
092:         * instances for sub-reports, you <i>must</i> specify a value for the
093:         * <code>reportDataKey</code> property, indicating the data to use for the main report.
094:         *
095:         * <p>Allows for exporter parameters to be configured declatively using the
096:         * <code>exporterParameters</code> property. This is a <code>Map</code> typed
097:         * property where the key of an entry corresponds to the fully-qualified name
098:         * of the static field for the <code>JRExporterParameter</code> and the value
099:         * of an entry is the value you want to assign to the exporter parameter.
100:         *
101:         * <p>Response headers can be controlled via the <code>headers</code> property. Spring
102:         * will attempt to set the correct value for the <code>Content-Diposition</code> header
103:         * so that reports render correctly in Internet Explorer. However, you can override this
104:         * setting through the <code>headers</code> property.
105:         *
106:         * @author Rob Harrop
107:         * @author Juergen Hoeller
108:         * @since 1.1.3
109:         * @see #setUrl
110:         * @see #setReportDataKey
111:         * @see #setSubReportUrls
112:         * @see #setSubReportDataKeys
113:         * @see #setHeaders
114:         * @see #setExporterParameters
115:         * @see #setJdbcDataSource
116:         */
117:        public abstract class AbstractJasperReportsView extends
118:                AbstractUrlBasedView {
119:
120:            /**
121:             * Constant that defines "Content-Disposition" header.
122:             */
123:            protected static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
124:
125:            /**
126:             * The default Content-Disposition header. Used to make IE play nice.
127:             */
128:            protected static final String CONTENT_DISPOSITION_INLINE = "inline";
129:
130:            /**
131:             * A String key used to lookup the <code>JRDataSource</code> in the model.
132:             */
133:            private String reportDataKey;
134:
135:            /**
136:             * Stores the paths to any sub-report files used by this top-level report,
137:             * along with the keys they are mapped to in the top-level report file.
138:             */
139:            private Properties subReportUrls;
140:
141:            /**
142:             * Stores the names of any data source objects that need to be converted to
143:             * <code>JRDataSource</code> instances and included in the report parameters
144:             * to be passed on to a sub-report.
145:             */
146:            private String[] subReportDataKeys;
147:
148:            /**
149:             * Stores the headers to written with each response
150:             */
151:            private Properties headers;
152:
153:            /**
154:             * Stores the exporter parameters passed in by the user as passed in by the user. May be keyed as
155:             * <code>String</code>s with the fully qualified name of the exporter parameter field.
156:             */
157:            private Map exporterParameters = new HashMap();
158:
159:            /**
160:             * Stores the converted exporter parameters - keyed by <code>JRExporterParameter</code>.
161:             */
162:            private Map convertedExporterParameters;
163:
164:            /**
165:             * Stores the <code>DataSource</code>, if any, used as the report data source.
166:             */
167:            private DataSource jdbcDataSource;
168:
169:            /**
170:             * Holds the JRCompiler implementation to use for compiling reports on-the-fly.
171:             */
172:            private JRCompiler reportCompiler = JRDefaultCompiler.getInstance();
173:
174:            /**
175:             * The <code>JasperReport</code> that is used to render the view.
176:             */
177:            private JasperReport report;
178:
179:            /**
180:             * Holds mappings between sub-report keys and <code>JasperReport</code> objects.
181:             */
182:            private Map subReports;
183:
184:            /**
185:             * Set the name of the model attribute that represents the report data.
186:             * If not specified, the model map will be searched for a matching value type.
187:             * <p>A <code>JRDataSource</code> will be taken as-is. For other types, conversion
188:             * will apply: By default, a <code>java.util.Collection</code> will be converted
189:             * to <code>JRBeanCollectionDataSource</code>, and an object array to
190:             * <code>JRBeanArrayDataSource</code>.
191:             * <p><b>Note:</b> If you pass in a Collection or object array in the model map
192:             * for use as plain report parameter, rather than as report data to extract fields
193:             * from, you need to specify the key for the actual report data to use, to avoid
194:             * mis-detection of report data by type.
195:             * @see #convertReportData
196:             * @see net.sf.jasperreports.engine.JRDataSource
197:             * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
198:             * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
199:             */
200:            public void setReportDataKey(String reportDataKey) {
201:                this .reportDataKey = reportDataKey;
202:            }
203:
204:            /**
205:             * Specify resource paths which must be loaded as instances of
206:             * <code>JasperReport</code> and passed to the JasperReports engine for
207:             * rendering as sub-reports, under the same keys as in this mapping.
208:             * @param subReports mapping between model keys and resource paths
209:             * (Spring resource locations)
210:             * @see #setUrl
211:             * @see org.springframework.context.ApplicationContext#getResource
212:             */
213:            public void setSubReportUrls(Properties subReports) {
214:                this .subReportUrls = subReports;
215:            }
216:
217:            /**
218:             * Set the list of names corresponding to the model parameters that will contain
219:             * data source objects for use in sub-reports. Spring will convert these objects
220:             * to instances of <code>JRDataSource</code> where applicable and will then
221:             * include the resulting <code>JRDataSource</code> in the parameters passed into
222:             * the JasperReports engine.
223:             * <p>The name specified in the list should correspond to an attribute in the
224:             * model Map, and to a sub-report data source parameter in your report file.
225:             * If you pass in <code>JRDataSource</code> objects as model attributes,
226:             * specifing this list of keys is not required.
227:             * <p>If you specify a list of sub-report data keys, it is required to also
228:             * specify a <code>reportDataKey</code> for the main report, to avoid confusion
229:             * between the data source objects for the various reports involved.
230:             * @param subReportDataKeys list of names for sub-report data source objects
231:             * @see #setReportDataKey
232:             * @see #convertReportData
233:             * @see net.sf.jasperreports.engine.JRDataSource
234:             * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
235:             * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
236:             */
237:            public void setSubReportDataKeys(String[] subReportDataKeys) {
238:                this .subReportDataKeys = subReportDataKeys;
239:            }
240:
241:            /**
242:             * Specify the set of headers that are included in each of response.
243:             * @param headers the headers to write to each response.
244:             */
245:            public void setHeaders(Properties headers) {
246:                this .headers = headers;
247:            }
248:
249:            /**
250:             * Set the exporter parameters that should be used when rendering a view.
251:             * @param parameters <code>Map</code> with the fully qualified field name
252:             * of the <code>JRExporterParameter</code> instance as key
253:             * (e.g. "net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI")
254:             * and the value you wish to assign to the parameter as value
255:             */
256:            public void setExporterParameters(Map parameters) {
257:                // NOTE: Removed conversion from here since configuration of parameters
258:                // can also happen through access to the underlying Map using
259:                // getExporterParameters(). Conversion now happens in initApplicationContext,
260:                // and subclasses use getConvertedExporterParameters() to access the converted
261:                // parameter Map - robh.
262:                this .exporterParameters = parameters;
263:            }
264:
265:            /**
266:             * Return the exporter parameters that this view uses, if any.
267:             */
268:            public Map getExporterParameters() {
269:                return this .exporterParameters;
270:            }
271:
272:            /**
273:             * Allows subclasses to retrieve the converted exporter parameters.
274:             */
275:            protected Map getConvertedExporterParameters() {
276:                return this .convertedExporterParameters;
277:            }
278:
279:            /**
280:             * Specify the <code>javax.sql.DataSource</code> to use for reports with
281:             * embedded SQL statements.
282:             */
283:            public void setJdbcDataSource(DataSource jdbcDataSource) {
284:                this .jdbcDataSource = jdbcDataSource;
285:            }
286:
287:            /**
288:             * Return the <code>javax.sql.DataSource</code> that this view uses, if any.
289:             */
290:            protected DataSource getJdbcDataSource() {
291:                return this .jdbcDataSource;
292:            }
293:
294:            /**
295:             * Specify the JRCompiler implementation to use for compiling a ".jrxml"
296:             * report file on-the-fly into a report class.
297:             * <p>By default, a JRDefaultCompiler will be used, delegating to the
298:             * Eclipse JDT compiler or the Sun JDK compiler underneath.
299:             * @see net.sf.jasperreports.engine.design.JRDefaultCompiler
300:             */
301:            public void setReportCompiler(JRCompiler reportCompiler) {
302:                this .reportCompiler = (reportCompiler != null ? reportCompiler
303:                        : JRDefaultCompiler.getInstance());
304:            }
305:
306:            /**
307:             * Return the JRCompiler instance to use for compiling ".jrxml" report files.
308:             */
309:            protected JRCompiler getReportCompiler() {
310:                return this .reportCompiler;
311:            }
312:
313:            /**
314:             * Checks to see that a valid report file URL is supplied in the
315:             * configuration. Compiles the report file is necessary.
316:             * <p/>Subclasses can add custom initialization logic by overriding
317:             * the {@link #onInit} method.
318:             * @see #onInit() 
319:             */
320:            protected final void initApplicationContext()
321:                    throws ApplicationContextException {
322:                Resource mainReport = getApplicationContext().getResource(
323:                        getUrl());
324:                this .report = loadReport(mainReport);
325:
326:                // Load sub reports if required, and check data source parameters.
327:                if (this .subReportUrls != null) {
328:                    if (this .subReportDataKeys != null
329:                            && this .subReportDataKeys.length > 0
330:                            && this .reportDataKey == null) {
331:                        throw new ApplicationContextException(
332:                                "'reportDataKey' for main report is required when specifying a value for 'subReportDataKeys'");
333:                    }
334:                    this .subReports = new HashMap(this .subReportUrls.size());
335:                    for (Enumeration urls = this .subReportUrls.propertyNames(); urls
336:                            .hasMoreElements();) {
337:                        String key = (String) urls.nextElement();
338:                        String path = this .subReportUrls.getProperty(key);
339:                        Resource resource = getApplicationContext()
340:                                .getResource(path);
341:                        this .subReports.put(key, loadReport(resource));
342:                    }
343:                }
344:
345:                // Convert user-supplied exporterParameters.
346:                convertExporterParameters();
347:
348:                if (this .headers == null) {
349:                    this .headers = new Properties();
350:                }
351:                if (!this .headers.containsKey(HEADER_CONTENT_DISPOSITION)) {
352:                    this .headers.setProperty(HEADER_CONTENT_DISPOSITION,
353:                            CONTENT_DISPOSITION_INLINE);
354:                }
355:
356:                onInit();
357:            }
358:
359:            /**
360:             * Subclasses can override this to add some custom initialization logic. Called
361:             * by {@link #initApplicationContext()} as soon as all standard initialization logic
362:             * has finished executing.
363:             * @see #initApplicationContext()
364:             */
365:            protected void onInit() {
366:            }
367:
368:            /**
369:             * Converts the exporter parameters passed in by the user which may be keyed
370:             * by <code>String</code>s corresponding to the fully qualified name of the
371:             * <code>JRExporterParameter</code> into parameters which are keyed by
372:             * <code>JRExporterParameter</code>.
373:             * @see #getExporterParameter(Object)
374:             */
375:            protected final void convertExporterParameters() {
376:                if (this .exporterParameters != null
377:                        && !this .exporterParameters.isEmpty()) {
378:                    this .convertedExporterParameters = new HashMap(
379:                            this .exporterParameters.size());
380:                    for (Iterator it = this .exporterParameters.entrySet()
381:                            .iterator(); it.hasNext();) {
382:                        Map.Entry entry = (Map.Entry) it.next();
383:                        JRExporterParameter exporterParameter = getExporterParameter(entry
384:                                .getKey());
385:                        this .convertedExporterParameters.put(exporterParameter,
386:                                convertParameterValue(exporterParameter, entry
387:                                        .getValue()));
388:                    }
389:                }
390:            }
391:
392:            /**
393:             * Convert the supplied parameter value into the actual type required by the
394:             * corresponding {@link JRExporterParameter}.
395:             * <p>The default implementation simply converts the String values "true" and
396:             * "false" into corresponding <code>Boolean</code> objects, and tries to convert
397:             * String values that start with a digit into <code>Integer</code> objects
398:             * (simply keeping them as String if number conversion fails).
399:             */
400:            protected Object convertParameterValue(
401:                    JRExporterParameter parameter, Object value) {
402:                if (value instanceof  String) {
403:                    String str = (String) value;
404:                    if ("true".equals(str)) {
405:                        return Boolean.TRUE;
406:                    } else if ("false".equals(str)) {
407:                        return Boolean.FALSE;
408:                    } else if (str.length() > 0
409:                            && Character.isDigit(str.charAt(0))) {
410:                        // Looks like a number... let's try.
411:                        try {
412:                            return new Integer(str);
413:                        } catch (NumberFormatException ex) {
414:                            // OK, then let's keep it as a String value.
415:                            return str;
416:                        }
417:                    }
418:                }
419:                return value;
420:            }
421:
422:            /**
423:             * Return a <code>JRExporterParameter</code> for the given parameter object,
424:             * converting it from a String if necessary.
425:             * @param parameter the parameter object, either a String or a JRExporterParameter
426:             * @return a JRExporterParameter for the given parameter object
427:             * @see #convertToExporterParameter(String)
428:             */
429:            protected JRExporterParameter getExporterParameter(Object parameter) {
430:                if (parameter instanceof  JRExporterParameter) {
431:                    return (JRExporterParameter) parameter;
432:                }
433:                if (parameter instanceof  String) {
434:                    return convertToExporterParameter((String) parameter);
435:                }
436:                throw new IllegalArgumentException(
437:                        "Parameter ["
438:                                + parameter
439:                                + "] is invalid type. Should be either String or JRExporterParameter.");
440:            }
441:
442:            /**
443:             * Convert the given fully qualified field name to a corresponding
444:             * JRExporterParameter instance.
445:             * @param fqFieldName the fully qualified field name, consisting
446:             * of the class name followed by a dot followed by the field name
447:             * (e.g. "net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI")
448:             * @return the corresponding JRExporterParameter instance
449:             */
450:            protected JRExporterParameter convertToExporterParameter(
451:                    String fqFieldName) {
452:                int index = fqFieldName.lastIndexOf('.');
453:                if (index == -1 || index == fqFieldName.length()) {
454:                    throw new IllegalArgumentException(
455:                            "Parameter name ["
456:                                    + fqFieldName
457:                                    + "] is not a valid static field. "
458:                                    + "The parameter name must map to a static field such as "
459:                                    + "[net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI]");
460:                }
461:                String className = fqFieldName.substring(0, index);
462:                String fieldName = fqFieldName.substring(index + 1);
463:
464:                try {
465:                    Class cls = ClassUtils.forName(className);
466:                    Field field = cls.getField(fieldName);
467:
468:                    if (JRExporterParameter.class.isAssignableFrom(field
469:                            .getType())) {
470:                        try {
471:                            return (JRExporterParameter) field.get(null);
472:                        } catch (IllegalAccessException ex) {
473:                            throw new IllegalArgumentException(
474:                                    "Unable to access field ["
475:                                            + fieldName
476:                                            + "] of class ["
477:                                            + className
478:                                            + "]. "
479:                                            + "Check that it is static and accessible.");
480:                        }
481:                    } else {
482:                        throw new IllegalArgumentException(
483:                                "Field ["
484:                                        + fieldName
485:                                        + "] on class ["
486:                                        + className
487:                                        + "] is not assignable from JRExporterParameter - check the type of this field.");
488:                    }
489:                } catch (ClassNotFoundException ex) {
490:                    throw new IllegalArgumentException("Class [" + className
491:                            + "] in key [" + fqFieldName
492:                            + "] could not be found.");
493:                } catch (NoSuchFieldException ex) {
494:                    throw new IllegalArgumentException("Field [" + fieldName
495:                            + "] in key [" + fqFieldName
496:                            + "] could not be found on class [" + className
497:                            + "].");
498:                }
499:            }
500:
501:            /**
502:             * Loads a <code>JasperReport</code> from the specified <code>Resource</code>. If
503:             * the <code>Resource</code> points to an uncompiled report design file then the
504:             * report file is compiled dynamically and loaded into memory.
505:             * @param resource the <code>Resource</code> containing the report definition or design
506:             * @return a <code>JasperReport</code> instance
507:             */
508:            private JasperReport loadReport(Resource resource)
509:                    throws ApplicationContextException {
510:                try {
511:                    String fileName = resource.getFilename();
512:                    if (fileName.endsWith(".jasper")) {
513:                        // Load pre-compiled report.
514:                        if (logger.isInfoEnabled()) {
515:                            logger
516:                                    .info("Loading pre-compiled Jasper Report from "
517:                                            + resource);
518:                        }
519:                        return (JasperReport) JRLoader.loadObject(resource
520:                                .getInputStream());
521:                    } else if (fileName.endsWith(".jrxml")) {
522:                        // Compile report on-the-fly.
523:                        if (logger.isInfoEnabled()) {
524:                            logger.info("Compiling Jasper Report loaded from "
525:                                    + resource);
526:                        }
527:                        JasperDesign design = JRXmlLoader.load(resource
528:                                .getInputStream());
529:                        return getReportCompiler().compileReport(design);
530:                    } else {
531:                        throw new IllegalArgumentException("Report URL ["
532:                                + getUrl()
533:                                + "] must end in either .jasper or .jrxml");
534:                    }
535:                } catch (IOException ex) {
536:                    throw new ApplicationContextException(
537:                            "Could not load JasperReports report for URL ["
538:                                    + getUrl() + "]", ex);
539:                } catch (JRException ex) {
540:                    throw new ApplicationContextException(
541:                            "Could not parse JasperReports report for URL ["
542:                                    + getUrl() + "]", ex);
543:                }
544:            }
545:
546:            /**
547:             * Finds the report data to use for rendering the report and then invokes the
548:             * <code>renderReport</code> method that should be implemented by the subclass.
549:             * @param model the model map, as passed in for view rendering. Must contain
550:             * a report data value that can be converted to a <code>JRDataSource</code>,
551:             * acccording to the <code>getReportData</code> method.
552:             * @see #getReportData
553:             */
554:            protected void renderMergedOutputModel(Map model,
555:                    HttpServletRequest request, HttpServletResponse response)
556:                    throws Exception {
557:
558:                if (this .subReports != null) {
559:                    // Expose sub-reports as model attributes.
560:                    model.putAll(this .subReports);
561:
562:                    // Transform any collections etc into JRDataSources for sub reports.
563:                    if (this .subReportDataKeys != null) {
564:                        for (int i = 0; i < this .subReportDataKeys.length; i++) {
565:                            String key = this .subReportDataKeys[i];
566:                            model.put(key, convertReportData(model.get(key)));
567:                        }
568:                    }
569:                }
570:
571:                // Expose Spring-managed Locale and MessageSource.
572:                exposeLocalizationContext(model, request);
573:
574:                // Fill the report.
575:                JasperPrint filledReport = fillReport(model);
576:                postProcessReport(filledReport, model);
577:
578:                // Prepare response and render report.
579:                response.reset();
580:                populateHeaders(response);
581:                renderReport(filledReport, model, response);
582:            }
583:
584:            /**
585:             * Expose current Spring-managed Locale and MessageSource to JasperReports i18n
586:             * ($R expressions etc). The MessageSource should only be exposed as JasperReports
587:             * resource bundle if no such bundle is defined in the report itself.
588:             * <p>Default implementation exposes the Spring RequestContext Locale and a
589:             * MessageSourceResourceBundle adapter for the Spring ApplicationContext,
590:             * analogous to the <code>JstlUtils.exposeLocalizationContext</code> method.
591:             * @see org.springframework.web.servlet.support.RequestContextUtils#getLocale
592:             * @see org.springframework.context.support.MessageSourceResourceBundle
593:             * @see #getApplicationContext()
594:             * @see net.sf.jasperreports.engine.JRParameter#REPORT_LOCALE
595:             * @see net.sf.jasperreports.engine.JRParameter#REPORT_RESOURCE_BUNDLE
596:             * @see org.springframework.web.servlet.support.JstlUtils#exposeLocalizationContext
597:             */
598:            protected void exposeLocalizationContext(Map model,
599:                    HttpServletRequest request) {
600:                Locale locale = RequestContextUtils.getLocale(request);
601:                model.put(JRParameter.REPORT_LOCALE, locale);
602:                if (this .report.getResourceBundle() == null) {
603:                    ResourceBundle bundle = new MessageSourceResourceBundle(
604:                            getApplicationContext(), locale);
605:                    model.put(JRParameter.REPORT_RESOURCE_BUNDLE, bundle);
606:                }
607:            }
608:
609:            /**
610:             * Create a populated <code>JasperPrint</code> instance from the configured
611:             * <code>JasperReport</code> instance.
612:             * <p>By default, thois method will use any <code>JRDataSource</code> instance
613:             * (or wrappable <code>Object</code>) that can be located using {@link #getReportData}.
614:             * If no <code>JRDataSource</code> can be found, this method will use a JDBC
615:             * <code>Connection</code> obtained from the configured <code>javax.sql.DataSource</code>
616:             * (or a DataSource attribute in the model). If no JDBC DataSource can be found
617:             * either, the JasperReports engine will be invoked with plain model Map,
618:             * assuming that the model contains parameters that identify the source
619:             * for report data (e.g. Hibernate or JPA queries).
620:             * @param model the model for this request
621:             * @throws IllegalArgumentException if no <code>JRDataSource</code> can be found
622:             * and no <code>javax.sql.DataSource</code> is supplied
623:             * @throws SQLException if there is an error when populating the report using
624:             * the <code>javax.sql.DataSource</code>
625:             * @throws JRException if there is an error when populating the report using
626:             * a <code>JRDataSource</code>
627:             * @return the populated <code>JasperPrint</code> instance
628:             * @see #getReportData
629:             * @see #setJdbcDataSource
630:             */
631:            protected JasperPrint fillReport(Map model)
632:                    throws IllegalArgumentException, SQLException, JRException {
633:                // Determine JRDataSource for main report.
634:                JRDataSource jrDataSource = getReportData(model);
635:
636:                if (jrDataSource != null) {
637:                    // Use the JasperReports JRDataSource.
638:                    if (logger.isDebugEnabled()) {
639:                        logger.debug("Filling report with JRDataSource ["
640:                                + jrDataSource + "].");
641:                    }
642:                    return JasperFillManager.fillReport(this .report, model,
643:                            jrDataSource);
644:                }
645:
646:                else {
647:                    if (this .jdbcDataSource == null) {
648:                        this .jdbcDataSource = (DataSource) CollectionUtils
649:                                .findValueOfType(model.values(),
650:                                        DataSource.class);
651:                    }
652:
653:                    if (this .jdbcDataSource != null) {
654:                        // Use the JDBC DataSource.
655:                        if (logger.isDebugEnabled()) {
656:                            logger
657:                                    .debug("Filling report with JDBC DataSource ["
658:                                            + this .jdbcDataSource + "].");
659:                        }
660:                        Connection con = this .jdbcDataSource.getConnection();
661:                        try {
662:                            return JasperFillManager.fillReport(this .report,
663:                                    model, con);
664:                        } finally {
665:                            try {
666:                                con.close();
667:                            } catch (SQLException ex) {
668:                                logger.debug("Could not close JDBC Connection",
669:                                        ex);
670:                            }
671:                        }
672:                    }
673:
674:                    else {
675:                        // Assume that the model contains parameters that identify
676:                        // the source for report data (e.g. Hibernate or JPA queries).
677:                        return JasperFillManager.fillReport(this .report, model);
678:                    }
679:                }
680:            }
681:
682:            /**
683:             * Populates the headers in the <code>HttpServletResponse</code> with the
684:             * headers supplied by the user.
685:             */
686:            private void populateHeaders(HttpServletResponse response) {
687:                // Apply the headers to the response.
688:                for (Enumeration en = this .headers.propertyNames(); en
689:                        .hasMoreElements();) {
690:                    String key = (String) en.nextElement();
691:                    response.addHeader(key, this .headers.getProperty(key));
692:                }
693:            }
694:
695:            /**
696:             * Find an instance of <code>JRDataSource</code> in the given model map or create an
697:             * appropriate JRDataSource for passed-in report data.
698:             * <p>The default implementation checks for a model object under the
699:             * specified "reportDataKey" first, then falls back to looking for a value
700:             * of type <code>JRDataSource</code>, <code>java.util.Collection</code>,
701:             * object array (in that order).
702:             * @param model the model map, as passed in for view rendering
703:             * @return the <code>JRDataSource</code> or <code>null</code> if the data source is not found
704:             * @see #setReportDataKey
705:             * @see #convertReportData
706:             * @see #getReportDataTypes
707:             */
708:            protected JRDataSource getReportData(Map model) {
709:                // Try model attribute with specified name.
710:                if (this .reportDataKey != null) {
711:                    Object value = model.get(this .reportDataKey);
712:                    return convertReportData(value);
713:                }
714:
715:                // Try to find matching attribute, of given prioritized types.
716:                Object value = CollectionUtils.findValueOfType(model.values(),
717:                        getReportDataTypes());
718:
719:                if (value != null) {
720:                    return convertReportData(value);
721:                }
722:
723:                return null;
724:            }
725:
726:            /**
727:             * Convert the given report data value to a <code>JRDataSource</code>.
728:             * <p>The default implementation delegates to <code>JasperReportUtils</code> unless
729:             * the report data value is an instance of <code>JRDataSourceProvider</code>.
730:             * A <code>JRDataSource</code>, <code>JRDataSourceProvider</code>,
731:             * <code>java.util.Collection</code> or object array is detected.
732:             * <code>JRDataSource</code>s are returned as is, whilst <code>JRDataSourceProvider</code>s
733:             * are used to create an instance of <code>JRDataSource</code> which is then returned.
734:             * The latter two are converted to <code>JRBeanCollectionDataSource</code> or
735:             * <code>JRBeanArrayDataSource</code>, respectively.
736:             * @param value the report data value to convert
737:             * @return the JRDataSource
738:             * @throws IllegalArgumentException if the value could not be converted
739:             * @see org.springframework.ui.jasperreports.JasperReportsUtils#convertReportData
740:             * @see net.sf.jasperreports.engine.JRDataSource
741:             * @see net.sf.jasperreports.engine.JRDataSourceProvider
742:             * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
743:             * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
744:             */
745:            protected JRDataSource convertReportData(Object value)
746:                    throws IllegalArgumentException {
747:                if (value instanceof  JRDataSourceProvider) {
748:                    try {
749:                        return ((JRDataSourceProvider) value)
750:                                .create(this .report);
751:                    } catch (JRException ex) {
752:                        throw new IllegalArgumentException(
753:                                "Supplied JRDataSourceProvider is invalid: "
754:                                        + ex);
755:                    }
756:                } else {
757:                    return JasperReportsUtils.convertReportData(value);
758:                }
759:            }
760:
761:            /**
762:             * Return the value types that can be converted to a <code>JRDataSource</code>,
763:             * in prioritized order. Should only return types that the
764:             * {@link #convertReportData} method is actually able to convert.
765:             * <p>Default value types are: <code>JRDataSource</code>,
766:             * <code>JRDataSourceProvider</code> <code>java.util.Collection</code>
767:             * and <code>Object</code> array.
768:             * @return the value types in prioritized order
769:             */
770:            protected Class[] getReportDataTypes() {
771:                return new Class[] { JRDataSource.class,
772:                        JRDataSourceProvider.class, Collection.class,
773:                        Object[].class };
774:            }
775:
776:            /**
777:             * Allows sub-classes to get access to the <code>JasperReport</code> instance
778:             * loaded by Spring.
779:             * @return an instance of <code>JasperReport</code>
780:             */
781:            protected JasperReport getReport() {
782:                return this .report;
783:            }
784:
785:            /**
786:             * Template method to be overridden for custom post-processing of the
787:             * populated report. Invoked after filling but before rendering.
788:             * <p>The default implementation is empty.
789:             * @param populatedReport the populated <code>JasperPrint</code>
790:             * @param model the map containing report parameters
791:             * @throws Exception if post-processing failed
792:             */
793:            protected void postProcessReport(JasperPrint populatedReport,
794:                    Map model) throws Exception {
795:            }
796:
797:            /**
798:             * Subclasses should implement this method to perform the actual rendering process.
799:             * <p>Note that the content type has not been set yet: Implementors should build
800:             * a content type String and set it via <code>response.setContentType</code>.
801:             * If necessary, this can include a charset clause for a specific encoding.
802:             * The latter will only be necessary for textual output onto a Writer, and only
803:             * in case of the encoding being specified in the JasperReports exporter parameters.
804:             * <p><b>WARNING:</b> Implementors should not use <code>response.setCharacterEncoding</code>
805:             * unless they are willing to depend on Servlet API 2.4 or higher. Prefer a
806:             * concatenated content type String with a charset clause instead.
807:             * @param populatedReport the populated <code>JasperPrint</code> to render
808:             * @param model the map containing report parameters
809:             * @param response the HTTP response the report should be rendered to
810:             * @throws Exception if rendering failed
811:             * @see #getContentType()
812:             * @see javax.servlet.ServletResponse#setContentType
813:             * @see javax.servlet.ServletResponse#setCharacterEncoding
814:             */
815:            protected abstract void renderReport(JasperPrint populatedReport,
816:                    Map model, HttpServletResponse response) throws Exception;
817:
818:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.