View Javadoc

1   /*
2    * JaspertReports JSF Plugin Copyright (C) 2011 A. Alonso Dominguez
3    *
4    * This library is free software; you can redistribute it and/or modify it
5    * under the terms of the GNU Lesser General Public License as published by
6    * the Free Software Foundation; either version 2.1 of the License, or (at
7    * your option) any later version. This library is distributed in the hope
8    * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9    * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   *
11   * See the GNU Lesser General Public License for more details. You should have
12   * received a copy of the GNU Lesser General Public License along with this
13   * library; if not, write to the Free Software Foundation, Inc., 59 Temple
14   * Place, Suite 330, Boston, MA 02111-1307 USA A.
15   *
16   * Alonso Dominguez
17   * alonsoft@users.sf.net
18   */
19  package net.sf.jasperreports.jsf.renderkit;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.net.URLEncoder;
25  import java.util.Collection;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.application.ViewHandler;
30  import javax.faces.component.UIComponent;
31  import javax.faces.context.FacesContext;
32  import javax.faces.render.Renderer;
33  
34  import net.sf.jasperreports.jsf.Constants;
35  import net.sf.jasperreports.jsf.component.UIReport;
36  import net.sf.jasperreports.jsf.config.Configuration;
37  import net.sf.jasperreports.jsf.engine.Exporter;
38  import net.sf.jasperreports.jsf.engine.Filler;
39  import net.sf.jasperreports.jsf.context.ContentType;
40  import net.sf.jasperreports.jsf.context.ExternalContextHelper;
41  import net.sf.jasperreports.jsf.context.JRFacesContext;
42  import net.sf.jasperreports.jsf.util.ComponentUtil;
43  import net.sf.jasperreports.jsf.util.Util;
44  
45  /**
46   *
47   * @author aalonsodominguez
48   */
49  public abstract class ReportRenderer extends Renderer {
50  
51      /** The logger instance */
52      private static final Logger logger = Logger.getLogger(
53              ReportRenderer.class.getPackage().getName(),
54              Constants.LOG_MESSAGES_BUNDLE);
55  
56      /**
57       * Obtain the specific content disposition for this kind of renderer.
58       *
59       * @return the content disposition.
60       */
61      public abstract String getContentDisposition();
62  
63      /**
64       * Encodes the report contents into the current response.
65       *
66       * @param context the faces' context.
67       * @param component the report component being rendered.
68       * @throws IOException if something happens when encoding the report.
69       */
70      public void encodeContent(final FacesContext context,
71              final UIReport component)
72              throws IOException {
73          if (context == null) {
74              throw new IllegalArgumentException("Faces' context is null");
75          }
76          if (component == null) {
77              throw new IllegalArgumentException("Report component is null");
78          }
79  
80          JRFacesContext jrContext = JRFacesContext.getInstance(context);
81          final String clientId = component.getClientId(context);
82  
83          final Filler filler = jrContext.getFiller(context, component);
84          logger.log(Level.FINE, "JRJSF_0006", clientId);
85          filler.fill(context, component);
86  
87          final ExternalContextHelper helper = jrContext
88                  .getExternalContextHelper(context);
89          final Exporter exporter = jrContext
90                  .getExporter(context, component);
91          
92          ByteArrayInputStream input = null;
93          final ByteArrayOutputStream output = new ByteArrayOutputStream();
94          try {
95              if (logger.isLoggable(Level.FINE)) {
96                  logger.log(Level.FINE, "JRJSF_0010", new Object[]{clientId,
97                              exporter.getContentTypes()});
98              }
99              exporter.export(context, component, output);
100             ContentType contentType = findAppropiateContentType(context, 
101             		component, exporter.getContentTypes());
102             if (contentType == null) {
103             	throw new UnsupportedExporterException(exporter.getClass().getName());
104             }
105             
106             input = new ByteArrayInputStream(output.toByteArray());
107             helper.writeResponse(context.getExternalContext(),
108                     contentType, input);
109         } finally {
110             try {
111                 output.close();
112             } catch (final IOException e) { ; }
113             if (input != null) {
114             	try {
115             		input.close();
116             	} catch (final IOException e) { ; }
117             }
118         }
119     }
120 
121     /**
122      * Encodes the HTTP <code>CONTENT-DISPOSITION</code> header string.
123      *
124      * @param component the report component.
125      * @param encoding current response encoding.
126      * @return An encoded <code>CONTENT-DISPOSITION</code> header value.
127      * @throws IOException if some error happens when encoding the
128      *                     report file name.
129      */
130     public String encodeContentDisposition(
131             final UIReport component, final String encoding)
132             throws IOException {
133         final StringBuffer disposition = new StringBuffer();
134         if (component.getName() != null) {
135             disposition.append(getContentDisposition());
136             disposition.append("; filename=");
137             disposition.append(
138                     URLEncoder.encode(component.getName(), encoding));
139         }
140         return disposition.toString();
141     }
142 
143     /**
144      * Encodes the required headers into the current response.
145      *
146      * @param context the faces' context.
147      * @param component the report component.
148      * @throws IOException if some error happens writing the headers values.
149      */
150     public void encodeHeaders(final FacesContext context,
151             final UIReport component)
152             throws IOException {
153         if (context == null) {
154             throw new IllegalArgumentException("Faces' context is null");
155         }
156         if (component == null) {
157             throw new IllegalArgumentException("Report component is null");
158         }
159 
160         final JRFacesContext jrContext = JRFacesContext.getInstance(context);
161         final ExternalContextHelper helper = jrContext
162                 .getExternalContextHelper(context);
163         helper.writeHeaders(context.getExternalContext(), this, component);
164     }
165 
166     /**
167      * Builds the report URL which will trigger the <code>RENDER_REPORT</code>
168      * phase of the plugin's lifecycle.
169      *
170      * @param context the faces' context.
171      * @param component the report component.
172      *
173      * @return the report URL.
174      */
175     public String encodeReportURL(final FacesContext context,
176             final UIComponent component) {
177         if (context == null) {
178             throw new IllegalArgumentException();
179         }
180         if (component == null) {
181             throw new IllegalArgumentException();
182         }
183 
184         JRFacesContext jrContext = JRFacesContext.getInstance(context);
185         final Configuration config = Configuration.getInstance(
186                 context.getExternalContext());
187         final ExternalContextHelper helper = jrContext
188                 .getExternalContextHelper(context);
189 
190         final StringBuffer reportURI = new StringBuffer(Constants.BASE_URI);
191         String name = ComponentUtil.getStringAttribute(component, "name", null);
192         if (name != null) {
193         	reportURI.append("/").append(name);
194         }
195         
196         String mapping = Util.getInvocationPath(context);
197         if (!config.getFacesMappings().contains(mapping)) {
198             if (logger.isLoggable(Level.WARNING)) {
199                 logger.log(Level.WARNING, "JRJSF_0021", new Object[]{mapping,
200                             config.getDefaultMapping()});
201             }
202             mapping = config.getDefaultMapping();
203         }
204 
205         if (Util.isPrefixMapped(mapping)) {
206             reportURI.insert(0, mapping);
207         } else {
208             reportURI.append(mapping);
209         }
210 
211         reportURI.append('?').append(Constants.PARAM_CLIENTID);
212         reportURI.append('=').append(component.getClientId(context));
213         reportURI.append('&').append(Constants.PARAM_VIEWID);
214         reportURI.append('=').append(helper.getViewId(
215                 context.getExternalContext()));
216 
217         final ViewHandler viewHandler = context.getApplication()
218                 .getViewHandler();
219         final String result = context.getExternalContext().encodeResourceURL(
220                 viewHandler.getResourceURL(context, reportURI.toString()));
221         if (logger.isLoggable(Level.FINER)) {
222             logger.log(Level.FINER, "JRJSF_0031", result);
223         }
224         return result;
225     }
226 
227     private ContentType findAppropiateContentType(FacesContext context, UIReport component, 
228     		Collection<ContentType> possibleValues) {
229     	ContentType result = null;
230     	
231     	String formatValue = ComponentUtil.getStringAttribute(component, "format", null);
232     	if (ContentType.isContentType(formatValue)) {
233     		ContentType format = new ContentType(formatValue);
234     		if (isContentTypeAccepted(context, format)) {
235     			result = format;
236     		}
237     	}
238     	
239     	if (result == null) {
240     		for (ContentType type : possibleValues) {
241     			if (isContentTypeAccepted(context, type)) {
242     				result = type;
243     				break;
244     			}
245     		}
246     	}
247     	
248     	return result;
249     }
250     
251     private boolean isContentTypeAccepted(FacesContext context, ContentType contentType) {
252     	JRFacesContext jrFacesContext = JRFacesContext.getInstance(context);
253     	ExternalContextHelper helper = jrFacesContext.getExternalContextHelper(context);
254     	for (ContentType accepted : helper.getAcceptedContentTypes(
255     			context.getExternalContext())) {
256     		if (accepted.implies(contentType)) {
257     			return true;
258     		}
259     	}
260     	return false;
261     }
262     
263     /**
264      * Establishes some flags into the faces' context so the plugin's lifecycle
265      * can perform the additional tasks needed to handle the
266      * <code>RENDER_REPORT</code> phase.
267      *
268      * @param context the faces' context.
269      */
270     protected final void registerReportView(final FacesContext context) {
271         if (Boolean.TRUE.equals(context.getExternalContext().getRequestMap()
272                 .get(Constants.ATTR_REPORT_VIEW))) {
273             return;
274         }
275 
276         JRFacesContext jrContext = JRFacesContext.getInstance(context);
277         final ExternalContextHelper helper = jrContext
278                 .getExternalContextHelper(context);
279         final String viewId = helper.getViewId(context.getExternalContext());
280 
281         context.getExternalContext().getRequestMap().put(
282                 Constants.ATTR_REPORT_VIEW, Boolean.TRUE);
283 
284         if (logger.isLoggable(Level.FINER)) {
285             logger.log(Level.FINER, "JRJSF_0013", viewId);
286         }
287     }
288 
289 }