001/*
002 * Copyright 2002-2014 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 *      https://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
017package org.springframework.web.servlet.view.jasperreports;
018
019import java.io.ByteArrayOutputStream;
020import java.util.Map;
021import javax.servlet.http.HttpServletResponse;
022
023import net.sf.jasperreports.engine.JasperPrint;
024
025import org.springframework.ui.jasperreports.JasperReportsUtils;
026import org.springframework.util.CollectionUtils;
027import org.springframework.web.util.WebUtils;
028
029/**
030 * Extends {@code AbstractJasperReportsView} to provide basic rendering logic
031 * for views that use a fixed format, e.g. always PDF or always HTML.
032 *
033 * <p>Subclasses need to implement two template methods: {@code createExporter}
034 * to create a JasperReports exporter for a specific output format, and
035 * {@code useWriter} to determine whether to write text or binary content.
036 *
037 * <p><b>This class is compatible with classic JasperReports releases back until 2.x.</b>
038 * As a consequence, it keeps using the {@link net.sf.jasperreports.engine.JRExporter}
039 * API which got deprecated as of JasperReports 5.5.2 (early 2014).
040 *
041 * @author Rob Harrop
042 * @author Juergen Hoeller
043 * @since 1.1.5
044 * @see #createExporter()
045 * @see #useWriter()
046 */
047@SuppressWarnings({"deprecation", "rawtypes"})
048public abstract class AbstractJasperReportsSingleFormatView extends AbstractJasperReportsView {
049
050        @Override
051        protected boolean generatesDownloadContent() {
052                return !useWriter();
053        }
054
055        /**
056         * Perform rendering for a single Jasper Reports exporter, that is,
057         * for a pre-defined output format.
058         */
059        @Override
060        @SuppressWarnings("unchecked")
061        protected void renderReport(JasperPrint populatedReport, Map<String, Object> model, HttpServletResponse response)
062                        throws Exception {
063
064                net.sf.jasperreports.engine.JRExporter exporter = createExporter();
065
066                Map<net.sf.jasperreports.engine.JRExporterParameter, Object> mergedExporterParameters = getConvertedExporterParameters();
067                if (!CollectionUtils.isEmpty(mergedExporterParameters)) {
068                        exporter.setParameters(mergedExporterParameters);
069                }
070
071                if (useWriter()) {
072                        renderReportUsingWriter(exporter, populatedReport, response);
073                }
074                else {
075                        renderReportUsingOutputStream(exporter, populatedReport, response);
076                }
077        }
078
079        /**
080         * We need to write text to the response Writer.
081         * @param exporter the JasperReports exporter to use
082         * @param populatedReport the populated {@code JasperPrint} to render
083         * @param response the HTTP response the report should be rendered to
084         * @throws Exception if rendering failed
085         */
086        protected void renderReportUsingWriter(net.sf.jasperreports.engine.JRExporter exporter,
087                        JasperPrint populatedReport, HttpServletResponse response) throws Exception {
088
089                // Copy the encoding configured for the report into the response.
090                String contentType = getContentType();
091                String encoding = (String) exporter.getParameter(net.sf.jasperreports.engine.JRExporterParameter.CHARACTER_ENCODING);
092                if (encoding != null) {
093                        // Only apply encoding if content type is specified but does not contain charset clause already.
094                        if (contentType != null && !contentType.toLowerCase().contains(WebUtils.CONTENT_TYPE_CHARSET_PREFIX)) {
095                                contentType = contentType + WebUtils.CONTENT_TYPE_CHARSET_PREFIX + encoding;
096                        }
097                }
098                response.setContentType(contentType);
099
100                // Render report into HttpServletResponse's Writer.
101                JasperReportsUtils.render(exporter, populatedReport, response.getWriter());
102        }
103
104        /**
105         * We need to write binary output to the response OutputStream.
106         * @param exporter the JasperReports exporter to use
107         * @param populatedReport the populated {@code JasperPrint} to render
108         * @param response the HTTP response the report should be rendered to
109         * @throws Exception if rendering failed
110         */
111        protected void renderReportUsingOutputStream(net.sf.jasperreports.engine.JRExporter exporter,
112                        JasperPrint populatedReport, HttpServletResponse response) throws Exception {
113
114                // IE workaround: write into byte array first.
115                ByteArrayOutputStream baos = createTemporaryOutputStream();
116                JasperReportsUtils.render(exporter, populatedReport, baos);
117                writeToResponse(response, baos);
118        }
119
120
121        /**
122         * Create a JasperReports exporter for a specific output format,
123         * which will be used to render the report to the HTTP response.
124         * <p>The {@code useWriter} method determines whether the
125         * output will be written as text or as binary content.
126         * @see #useWriter()
127         */
128        protected abstract net.sf.jasperreports.engine.JRExporter createExporter();
129
130        /**
131         * Return whether to use a {@code java.io.Writer} to write text content
132         * to the HTTP response. Else, a {@code java.io.OutputStream} will be used,
133         * to write binary content to the response.
134         * @see javax.servlet.ServletResponse#getWriter()
135         * @see javax.servlet.ServletResponse#getOutputStream()
136         */
137        protected abstract boolean useWriter();
138
139}