alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Spring Framework example source code file (AbstractJasperReportsView.java)

This example Spring Framework source code file (AbstractJasperReportsView.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Spring Framework tags/keywords

applicationcontextexception, class, datasource, illegalargumentexception, illegalargumentexception, jasperreport, jasperreport, jdbc, jrdatasource, jrexporterparameter, jrexporterparameter, map, object, request, response, servlet, sql, string, string, util

The Spring Framework AbstractJasperReportsView.java source code

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.view.jasperreports;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRDataSourceProvider;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JRCompiler;
import net.sf.jasperreports.engine.design.JRDefaultCompiler;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

import org.springframework.context.ApplicationContextException;
import org.springframework.context.support.MessageSourceResourceBundle;
import org.springframework.core.io.Resource;
import org.springframework.ui.jasperreports.JasperReportsUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.view.AbstractUrlBasedView;

/**
 * Base class for all JasperReports views. Applies on-the-fly compilation
 * of report designs as required and coordinates the rendering process.
 * The resource path of the main report needs to be specified as <code>url.
 *
 * <p>This class is responsible for getting report data from the model that has
 * been provided to the view. The default implementation checks for a model object
 * under the specified <code>reportDataKey first, then falls back to looking
 * for a value of type <code>JRDataSource, java.util.Collection,
 * object array (in that order).
 *
 * <p>If no JRDataSource can be found in the model, then reports will
 * be filled using the configured <code>javax.sql.DataSource if any. If neither
 * a <code>JRDataSource or javax.sql.DataSource is available then
 * an <code>IllegalArgumentException is raised.
 *
 * <p>Provides support for sub-reports through the subReportUrls and
 * <code>subReportDataKeys properties.
 *
 * <p>When using sub-reports, the master report should be configured using the
 * <code>url property and the sub-reports files should be configured using
 * the <code>subReportUrls property. Each entry in the subReportUrls
 * Map corresponds to an individual sub-report. The key of an entry must match up
 * to a sub-report parameter in your report file of type
 * <code>net.sf.jasperreports.engine.JasperReport,
 * and the value of an entry must be the URL for the sub-report file.
 *
 * <p>For sub-reports that require an instance of JRDataSource, that is,
 * they don't have a hard-coded query for data retrieval, you can include the
 * appropriate data in your model as would with the data source for the parent report.
 * However, you must provide a List of parameter names that need to be converted to
 * <code>JRDataSource instances for the sub-report via the
 * <code>subReportDataKeys property. When using JRDataSource
 * instances for sub-reports, you <i>must specify a value for the
 * <code>reportDataKey property, indicating the data to use for the main report.
 *
 * <p>Allows for exporter parameters to be configured declatively using the
 * <code>exporterParameters property. This is a Map typed
 * property where the key of an entry corresponds to the fully-qualified name
 * of the static field for the <code>JRExporterParameter and the value
 * of an entry is the value you want to assign to the exporter parameter.
 *
 * <p>Response headers can be controlled via the headers property. Spring
 * will attempt to set the correct value for the <code>Content-Diposition header
 * so that reports render correctly in Internet Explorer. However, you can override this
 * setting through the <code>headers property.
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @since 1.1.3
 * @see #setUrl
 * @see #setReportDataKey
 * @see #setSubReportUrls
 * @see #setSubReportDataKeys
 * @see #setHeaders
 * @see #setExporterParameters
 * @see #setJdbcDataSource
 */
public abstract class AbstractJasperReportsView extends AbstractUrlBasedView {

	/**
	 * Constant that defines "Content-Disposition" header.
	 */
	protected static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";

	/**
	 * The default Content-Disposition header. Used to make IE play nice.
	 */
	protected static final String CONTENT_DISPOSITION_INLINE = "inline";


	/**
	 * A String key used to lookup the <code>JRDataSource in the model.
	 */
	private String reportDataKey;

	/**
	 * Stores the paths to any sub-report files used by this top-level report,
	 * along with the keys they are mapped to in the top-level report file.
	 */
	private Properties subReportUrls;

	/**
	 * Stores the names of any data source objects that need to be converted to
	 * <code>JRDataSource instances and included in the report parameters
	 * to be passed on to a sub-report.
	 */
	private String[] subReportDataKeys;

	/**
	 * Stores the headers to written with each response
	 */
	private Properties headers;

	/**
	 * Stores the exporter parameters passed in by the user as passed in by the user. May be keyed as
	 * <code>Strings with the fully qualified name of the exporter parameter field.
	 */
	private Map exporterParameters = new HashMap();

	/**
	 * Stores the converted exporter parameters - keyed by <code>JRExporterParameter.
	 */
	private Map convertedExporterParameters;

	/**
	 * Stores the <code>DataSource, if any, used as the report data source.
	 */
	private DataSource jdbcDataSource;

	/**
	 * Holds the JRCompiler implementation to use for compiling reports on-the-fly.
	 */
	private JRCompiler reportCompiler = JRDefaultCompiler.getInstance();

	/**
	 * The <code>JasperReport that is used to render the view.
	 */
	private JasperReport report;

	/**
	 * Holds mappings between sub-report keys and <code>JasperReport objects.
	 */
	private Map subReports;


	/**
	 * Set the name of the model attribute that represents the report data.
	 * If not specified, the model map will be searched for a matching value type.
	 * <p>A JRDataSource will be taken as-is. For other types, conversion
	 * will apply: By default, a <code>java.util.Collection will be converted
	 * to <code>JRBeanCollectionDataSource, and an object array to
	 * <code>JRBeanArrayDataSource.
	 * <p>Note: If you pass in a Collection or object array in the model map
	 * for use as plain report parameter, rather than as report data to extract fields
	 * from, you need to specify the key for the actual report data to use, to avoid
	 * mis-detection of report data by type.
	 * @see #convertReportData
	 * @see net.sf.jasperreports.engine.JRDataSource
	 * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
	 * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
	 */
	public void setReportDataKey(String reportDataKey) {
		this.reportDataKey = reportDataKey;
	}

	/**
	 * Specify resource paths which must be loaded as instances of
	 * <code>JasperReport and passed to the JasperReports engine for
	 * rendering as sub-reports, under the same keys as in this mapping.
	 * @param subReports mapping between model keys and resource paths
	 * (Spring resource locations)
	 * @see #setUrl
	 * @see org.springframework.context.ApplicationContext#getResource
	 */
	public void setSubReportUrls(Properties subReports) {
		this.subReportUrls = subReports;
	}

	/**
	 * Set the list of names corresponding to the model parameters that will contain
	 * data source objects for use in sub-reports. Spring will convert these objects
	 * to instances of <code>JRDataSource where applicable and will then
	 * include the resulting <code>JRDataSource in the parameters passed into
	 * the JasperReports engine.
	 * <p>The name specified in the list should correspond to an attribute in the
	 * model Map, and to a sub-report data source parameter in your report file.
	 * If you pass in <code>JRDataSource objects as model attributes,
	 * specifing this list of keys is not required.
	 * <p>If you specify a list of sub-report data keys, it is required to also
	 * specify a <code>reportDataKey for the main report, to avoid confusion
	 * between the data source objects for the various reports involved.
	 * @param subReportDataKeys list of names for sub-report data source objects
	 * @see #setReportDataKey
	 * @see #convertReportData
	 * @see net.sf.jasperreports.engine.JRDataSource
	 * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
	 * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
	 */
	public void setSubReportDataKeys(String[] subReportDataKeys) {
		this.subReportDataKeys = subReportDataKeys;
	}

	/**
	 * Specify the set of headers that are included in each of response.
	 * @param headers the headers to write to each response.
	 */
	public void setHeaders(Properties headers) {
		this.headers = headers;
	}

	/**
	 * Set the exporter parameters that should be used when rendering a view.
	 * @param parameters <code>Map with the fully qualified field name
	 * of the <code>JRExporterParameter instance as key
	 * (e.g. "net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI")
	 * and the value you wish to assign to the parameter as value
	 */
	public void setExporterParameters(Map parameters) {
		// NOTE: Removed conversion from here since configuration of parameters
		// can also happen through access to the underlying Map using
		// getExporterParameters(). Conversion now happens in initApplicationContext,
		// and subclasses use getConvertedExporterParameters() to access the converted
		// parameter Map - robh.
		this.exporterParameters = parameters;
	}

	/**
	 * Return the exporter parameters that this view uses, if any.
	 */
	public Map getExporterParameters() {
		return this.exporterParameters;
	}

	/**
	 * Allows subclasses to retrieve the converted exporter parameters.
	 */
	protected Map getConvertedExporterParameters() {
		return this.convertedExporterParameters;
	}

	/**
	 * Specify the <code>javax.sql.DataSource to use for reports with
	 * embedded SQL statements.
	 */
	public void setJdbcDataSource(DataSource jdbcDataSource) {
		this.jdbcDataSource = jdbcDataSource;
	}

	/**
	 * Return the <code>javax.sql.DataSource that this view uses, if any.
	 */
	protected DataSource getJdbcDataSource() {
		return this.jdbcDataSource;
	}

	/**
	 * Specify the JRCompiler implementation to use for compiling a ".jrxml"
	 * report file on-the-fly into a report class.
	 * <p>By default, a JRDefaultCompiler will be used, delegating to the
	 * Eclipse JDT compiler or the Sun JDK compiler underneath.
	 * @see net.sf.jasperreports.engine.design.JRDefaultCompiler
	 */
	public void setReportCompiler(JRCompiler reportCompiler) {
		this.reportCompiler = (reportCompiler != null ? reportCompiler : JRDefaultCompiler.getInstance());
	}

	/**
	 * Return the JRCompiler instance to use for compiling ".jrxml" report files.
	 */
	protected JRCompiler getReportCompiler() {
		return this.reportCompiler;
	}


	/**
	 * JasperReports views do not strictly required a 'url' value.
	 * Alternatively, the {@link #getReport()} template method may be overridden.
	 */
	protected boolean isUrlRequired() {
		return false;
	}

	/**
	 * Checks to see that a valid report file URL is supplied in the
	 * configuration. Compiles the report file is necessary.
	 * <p>Subclasses can add custom initialization logic by overriding
	 * the {@link #onInit} method.
	 */
	protected final void initApplicationContext() throws ApplicationContextException {
		this.report = loadReport();

		// Load sub reports if required, and check data source parameters.
		if (this.subReportUrls != null) {
			if (this.subReportDataKeys != null && this.subReportDataKeys.length > 0 && this.reportDataKey == null) {
				throw new ApplicationContextException(
						"'reportDataKey' for main report is required when specifying a value for 'subReportDataKeys'");
			}
			this.subReports = new HashMap(this.subReportUrls.size());
			for (Enumeration urls = this.subReportUrls.propertyNames(); urls.hasMoreElements();) {
				String key = (String) urls.nextElement();
				String path = this.subReportUrls.getProperty(key);
				Resource resource = getApplicationContext().getResource(path);
				this.subReports.put(key, loadReport(resource));
			}
		}

		// Convert user-supplied exporterParameters.
		convertExporterParameters();

		if (this.headers == null) {
			this.headers = new Properties();
		}
		if (!this.headers.containsKey(HEADER_CONTENT_DISPOSITION)) {
			this.headers.setProperty(HEADER_CONTENT_DISPOSITION, CONTENT_DISPOSITION_INLINE);
		}

		onInit();
	}

	/**
	 * Subclasses can override this to add some custom initialization logic. Called
	 * by {@link #initApplicationContext()} as soon as all standard initialization logic
	 * has finished executing.
	 * @see #initApplicationContext()
	 */
	protected void onInit() {
	}

	/**
	 * Converts the exporter parameters passed in by the user which may be keyed
	 * by <code>Strings corresponding to the fully qualified name of the
	 * <code>JRExporterParameter into parameters which are keyed by
	 * <code>JRExporterParameter.
	 * @see #getExporterParameter(Object)
	 */
	protected final void convertExporterParameters() {
		if (!CollectionUtils.isEmpty(this.exporterParameters)) {
			this.convertedExporterParameters = new HashMap(this.exporterParameters.size());
			for (Iterator it = this.exporterParameters.entrySet().iterator(); it.hasNext();) {
				Map.Entry entry = (Map.Entry) it.next();
				JRExporterParameter exporterParameter = getExporterParameter(entry.getKey());
				this.convertedExporterParameters.put(
						exporterParameter, convertParameterValue(exporterParameter, entry.getValue()));
			}
		}
	}

	/**
	 * Convert the supplied parameter value into the actual type required by the
	 * corresponding {@link JRExporterParameter}.
	 * <p>The default implementation simply converts the String values "true" and
	 * "false" into corresponding <code>Boolean objects, and tries to convert
	 * String values that start with a digit into <code>Integer objects
	 * (simply keeping them as String if number conversion fails).
	 * @param parameter the parameter key
	 * @param value the parameter value
	 * @return the converted parameter value
	 */
	protected Object convertParameterValue(JRExporterParameter parameter, Object value) {
		if (value instanceof String) {
			String str = (String) value;
			if ("true".equals(str)) {
				return Boolean.TRUE;
			}
			else if ("false".equals(str)) {
				return Boolean.FALSE;
			}
			else if (str.length() > 0 && Character.isDigit(str.charAt(0))) {
				// Looks like a number... let's try.
				try {
					return new Integer(str);
				}
				catch (NumberFormatException ex) {
					// OK, then let's keep it as a String value.
					return str;
				}
			}
		}
		return value;
	}

	/**
	 * Return a <code>JRExporterParameter for the given parameter object,
	 * converting it from a String if necessary.
	 * @param parameter the parameter object, either a String or a JRExporterParameter
	 * @return a JRExporterParameter for the given parameter object
	 * @see #convertToExporterParameter(String)
	 */
	protected JRExporterParameter getExporterParameter(Object parameter) {
		if (parameter instanceof JRExporterParameter) {
			return (JRExporterParameter) parameter;
		}
		if (parameter instanceof String) {
			return convertToExporterParameter((String) parameter);
		}
		throw new IllegalArgumentException(
				"Parameter [" + parameter + "] is invalid type. Should be either String or JRExporterParameter.");
	}

	/**
	 * Convert the given fully qualified field name to a corresponding
	 * JRExporterParameter instance.
	 * @param fqFieldName the fully qualified field name, consisting
	 * of the class name followed by a dot followed by the field name
	 * (e.g. "net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI")
	 * @return the corresponding JRExporterParameter instance
	 */
	protected JRExporterParameter convertToExporterParameter(String fqFieldName) {
		int index = fqFieldName.lastIndexOf('.');
		if (index == -1 || index == fqFieldName.length()) {
			throw new IllegalArgumentException(
					"Parameter name [" + fqFieldName + "] is not a valid static field. " +
					"The parameter name must map to a static field such as " +
					"[net.sf.jasperreports.engine.export.JRHtmlExporterParameter.IMAGES_URI]");
		}
		String className = fqFieldName.substring(0, index);
		String fieldName = fqFieldName.substring(index + 1);

		try {
			Class cls = ClassUtils.forName(className);
			Field field = cls.getField(fieldName);

			if (JRExporterParameter.class.isAssignableFrom(field.getType())) {
				try {
					return (JRExporterParameter) field.get(null);
				}
				catch (IllegalAccessException ex) {
					throw new IllegalArgumentException(
							"Unable to access field [" + fieldName + "] of class [" + className + "]. " +
							"Check that it is static and accessible.");
				}
			}
			else {
				throw new IllegalArgumentException("Field [" + fieldName + "] on class [" + className +
						"] is not assignable from JRExporterParameter - check the type of this field.");
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalArgumentException(
					"Class [" + className + "] in key [" + fqFieldName + "] could not be found.");
		}
		catch (NoSuchFieldException ex) {
			throw new IllegalArgumentException("Field [" + fieldName + "] in key [" + fqFieldName +
					"] could not be found on class [" + className + "].");
		}
	}

	/**
	 * Load the main <code>JasperReport from the specified Resource.
	 * If the <code>Resource points to an uncompiled report design file then the
	 * report file is compiled dynamically and loaded into memory.
	 * @return a <code>JasperReport instance, or null if no main
	 * report has been statically defined
	 */
	protected JasperReport loadReport() {
		String url = getUrl();
		if (url == null) {
			return null;
		}
		Resource mainReport = getApplicationContext().getResource(url);
		return loadReport(mainReport);
	}

	/**
	 * Loads a <code>JasperReport from the specified Resource.
	 * If the <code>Resource points to an uncompiled report design file then
	 * the report file is compiled dynamically and loaded into memory.
	 * @param resource the <code>Resource containing the report definition or design
	 * @return a <code>JasperReport instance
	 */
	protected final JasperReport loadReport(Resource resource) {
		try {
			String fileName = resource.getFilename();
			if (fileName.endsWith(".jasper")) {
				// Load pre-compiled report.
				if (logger.isInfoEnabled()) {
					logger.info("Loading pre-compiled Jasper Report from " + resource);
				}
				return (JasperReport) JRLoader.loadObject(resource.getInputStream());
			}
			else if (fileName.endsWith(".jrxml")) {
				// Compile report on-the-fly.
				if (logger.isInfoEnabled()) {
					logger.info("Compiling Jasper Report loaded from " + resource);
				}
				JasperDesign design = JRXmlLoader.load(resource.getInputStream());
				return getReportCompiler().compileReport(design);
			}
			else {
				throw new IllegalArgumentException(
						"Report filename [" + fileName + "] must end in either .jasper or .jrxml");
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException(
					"Could not load JasperReports report from " + resource, ex);
		}
		catch (JRException ex) {
			throw new ApplicationContextException(
					"Could not parse JasperReports report from " + resource, ex);
		}
	}


	/**
	 * Finds the report data to use for rendering the report and then invokes the
	 * <code>renderReport method that should be implemented by the subclass.
	 * @param model the model map, as passed in for view rendering. Must contain
	 * a report data value that can be converted to a <code>JRDataSource,
	 * acccording to the <code>getReportData method.
	 * @see #getReportData
	 */
	protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		if (this.subReports != null) {
			// Expose sub-reports as model attributes.
			model.putAll(this.subReports);

			// Transform any collections etc into JRDataSources for sub reports.
			if (this.subReportDataKeys != null) {
				for (int i = 0; i < this.subReportDataKeys.length; i++) {
					String key = this.subReportDataKeys[i];
					model.put(key, convertReportData(model.get(key)));
				}
			}
		}

		// Expose Spring-managed Locale and MessageSource.
		exposeLocalizationContext(model, request);

		// Fill the report.
		JasperPrint filledReport = fillReport(model);
		postProcessReport(filledReport, model);

		// Prepare response and render report.
		populateHeaders(response);
		renderReport(filledReport, model, response);
	}

	/**
	 * Expose current Spring-managed Locale and MessageSource to JasperReports i18n
	 * ($R expressions etc). The MessageSource should only be exposed as JasperReports
	 * resource bundle if no such bundle is defined in the report itself.
	 * <p>Default implementation exposes the Spring RequestContext Locale and a
	 * MessageSourceResourceBundle adapter for the Spring ApplicationContext,
	 * analogous to the <code>JstlUtils.exposeLocalizationContext method.
	 * @see org.springframework.web.servlet.support.RequestContextUtils#getLocale
	 * @see org.springframework.context.support.MessageSourceResourceBundle
	 * @see #getApplicationContext()
	 * @see net.sf.jasperreports.engine.JRParameter#REPORT_LOCALE
	 * @see net.sf.jasperreports.engine.JRParameter#REPORT_RESOURCE_BUNDLE
	 * @see org.springframework.web.servlet.support.JstlUtils#exposeLocalizationContext
	 */
	protected void exposeLocalizationContext(Map model, HttpServletRequest request) {
		RequestContext rc = new RequestContext(request, getServletContext());
		model.put(JRParameter.REPORT_LOCALE, rc.getLocale());
		JasperReport report = getReport();
		if (report == null || report.getResourceBundle() == null) {
			model.put(JRParameter.REPORT_RESOURCE_BUNDLE,
					new MessageSourceResourceBundle(rc.getMessageSource(), rc.getLocale()));
		}
	}

	/**
	 * Create a populated <code>JasperPrint instance from the configured
	 * <code>JasperReport instance.
	 * <p>By default, thois method will use any JRDataSource instance
	 * (or wrappable <code>Object) that can be located using {@link #getReportData}.
	 * If no <code>JRDataSource can be found, this method will use a JDBC
	 * <code>Connection obtained from the configured javax.sql.DataSource
	 * (or a DataSource attribute in the model). If no JDBC DataSource can be found
	 * either, the JasperReports engine will be invoked with plain model Map,
	 * assuming that the model contains parameters that identify the source
	 * for report data (e.g. Hibernate or JPA queries).
	 * @param model the model for this request
	 * @throws IllegalArgumentException if no <code>JRDataSource can be found
	 * and no <code>javax.sql.DataSource is supplied
	 * @throws SQLException if there is an error when populating the report using
	 * the <code>javax.sql.DataSource
	 * @throws JRException if there is an error when populating the report using
	 * a <code>JRDataSource
	 * @return the populated <code>JasperPrint instance
	 * @see #getReportData
	 * @see #setJdbcDataSource
	 */
	protected JasperPrint fillReport(Map model) throws IllegalArgumentException, SQLException, JRException {
		// Determine main report.
		JasperReport report = getReport();
		if (report == null) {
			throw new IllegalStateException("No main report defined for 'fillReport' - " +
					"specify a 'url' on this view or override 'getReport()' or 'fillReport(Map)'");
		}

		// Determine JRDataSource for main report.
		JRDataSource jrDataSource = getReportData(model);
		if (jrDataSource != null) {
			// Use the JasperReports JRDataSource.
			if (logger.isDebugEnabled()) {
				logger.debug("Filling report with JRDataSource [" + jrDataSource + "].");
			}
			return JasperFillManager.fillReport(report, model, jrDataSource);
		}

		else {
			if (this.jdbcDataSource == null) {
				this.jdbcDataSource = (DataSource) CollectionUtils.findValueOfType(model.values(), DataSource.class);
			}

			if (this.jdbcDataSource != null) {
				// Use the JDBC DataSource.
				if (logger.isDebugEnabled()) {
					logger.debug("Filling report with JDBC DataSource [" + this.jdbcDataSource + "].");
				}
				Connection con = this.jdbcDataSource.getConnection();
				try {
					return JasperFillManager.fillReport(report, model, con);
				}
				finally {
					try {
						con.close();
					}
					catch (Throwable ex) {
						logger.debug("Could not close JDBC Connection", ex);
					}
				}
			}

			else {
				// Assume that the model contains parameters that identify
				// the source for report data (e.g. Hibernate or JPA queries).
				return JasperFillManager.fillReport(report, model);
			}
		}
	}

	/**
	 * Populates the headers in the <code>HttpServletResponse with the
	 * headers supplied by the user.
	 */
	private void populateHeaders(HttpServletResponse response) {
		// Apply the headers to the response.
		for (Enumeration en = this.headers.propertyNames(); en.hasMoreElements();) {
			String key = (String) en.nextElement();
			response.addHeader(key, this.headers.getProperty(key));
		}
	}

	/**
	 * Determine the <code>JasperReport to fill.
	 * Called by {@link #fillReport}.
	 * <p>The default implementation returns the report as statically configured
	 * through the 'url' property (and loaded by {@link #loadReport()}).
	 * Can be overridden in subclasses in order to dynamically obtain a
	 * <code>JasperReport instance. As an alternative, consider
	 * overriding the {@link #fillReport} template method itself.
	 * @return an instance of <code>JasperReport
	 */
	protected JasperReport getReport() {
		return this.report;
	}

	/**
	 * Find an instance of <code>JRDataSource in the given model map or create an
	 * appropriate JRDataSource for passed-in report data.
	 * <p>The default implementation checks for a model object under the
	 * specified "reportDataKey" first, then falls back to looking for a value
	 * of type <code>JRDataSource, java.util.Collection,
	 * object array (in that order).
	 * @param model the model map, as passed in for view rendering
	 * @return the <code>JRDataSource or null if the data source is not found
	 * @see #setReportDataKey
	 * @see #convertReportData
	 * @see #getReportDataTypes
	 */
	protected JRDataSource getReportData(Map model) {
		// Try model attribute with specified name.
		if (this.reportDataKey != null) {
			Object value = model.get(this.reportDataKey);
			return convertReportData(value);
		}

		// Try to find matching attribute, of given prioritized types.
		Object value = CollectionUtils.findValueOfType(model.values(), getReportDataTypes());

		if (value != null) {
			return convertReportData(value);
		}

		return null;
	}

	/**
	 * Convert the given report data value to a <code>JRDataSource.
	 * <p>The default implementation delegates to JasperReportUtils unless
	 * the report data value is an instance of <code>JRDataSourceProvider.
	 * A <code>JRDataSource, JRDataSourceProvider,
	 * <code>java.util.Collection or object array is detected.
	 * <code>JRDataSources are returned as is, whilst JRDataSourceProviders
	 * are used to create an instance of <code>JRDataSource which is then returned.
	 * The latter two are converted to <code>JRBeanCollectionDataSource or
	 * <code>JRBeanArrayDataSource, respectively.
	 * @param value the report data value to convert
	 * @return the JRDataSource
	 * @throws IllegalArgumentException if the value could not be converted
	 * @see org.springframework.ui.jasperreports.JasperReportsUtils#convertReportData
	 * @see net.sf.jasperreports.engine.JRDataSource
	 * @see net.sf.jasperreports.engine.JRDataSourceProvider
	 * @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
	 * @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
	 */
	protected JRDataSource convertReportData(Object value) throws IllegalArgumentException {
		if (value instanceof JRDataSourceProvider) {
			try {
				JasperReport report = getReport();
				if (report == null) {
					throw new IllegalStateException("No main report defined for JRDataSourceProvider - " +
							"specify a 'url' on this view or override 'getReport()'");
				}
				return ((JRDataSourceProvider) value).create(report);
			}
			catch (JRException ex) {
				throw new IllegalArgumentException("Supplied JRDataSourceProvider is invalid: " + ex);
			}
		}
		else {
			return JasperReportsUtils.convertReportData(value);
		}
	}

	/**
	 * Return the value types that can be converted to a <code>JRDataSource,
	 * in prioritized order. Should only return types that the
	 * {@link #convertReportData} method is actually able to convert.
	 * <p>Default value types are: JRDataSource,
	 * <code>JRDataSourceProvider java.util.Collection
	 * and <code>Object array.
	 * @return the value types in prioritized order
	 */
	protected Class[] getReportDataTypes() {
		return new Class[] {JRDataSource.class, JRDataSourceProvider.class, Collection.class, Object[].class};
	}


	/**
	 * Template method to be overridden for custom post-processing of the
	 * populated report. Invoked after filling but before rendering.
	 * <p>The default implementation is empty.
	 * @param populatedReport the populated <code>JasperPrint
	 * @param model the map containing report parameters
	 * @throws Exception if post-processing failed
	 */
	protected void postProcessReport(JasperPrint populatedReport, Map model) throws Exception {
	}

	/**
	 * Subclasses should implement this method to perform the actual rendering process.
	 * <p>Note that the content type has not been set yet: Implementors should build
	 * a content type String and set it via <code>response.setContentType.
	 * If necessary, this can include a charset clause for a specific encoding.
	 * The latter will only be necessary for textual output onto a Writer, and only
	 * in case of the encoding being specified in the JasperReports exporter parameters.
	 * <p>WARNING: Implementors should not use response.setCharacterEncoding
	 * unless they are willing to depend on Servlet API 2.4 or higher. Prefer a
	 * concatenated content type String with a charset clause instead.
	 * @param populatedReport the populated <code>JasperPrint to render
	 * @param model the map containing report parameters
	 * @param response the HTTP response the report should be rendered to
	 * @throws Exception if rendering failed
	 * @see #getContentType()
	 * @see javax.servlet.ServletResponse#setContentType
	 * @see javax.servlet.ServletResponse#setCharacterEncoding
	 */
	protected abstract void renderReport(JasperPrint populatedReport, Map model, HttpServletResponse response)
			throws Exception;

}

Other Spring Framework examples (source code examples)

Here is a short list of links related to this Spring Framework AbstractJasperReportsView.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.