View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.webapp.filter;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.util.Iterator;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.Application;
27  import javax.faces.application.FacesMessage;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseStream;
32  import javax.faces.context.ResponseWriter;
33  import javax.faces.render.RenderKit;
34  import javax.servlet.ServletContext;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.apache.commons.fileupload.servlet.ServletFileUpload;
39  import org.apache.myfaces.renderkit.html.util.AddResource;
40  import org.apache.myfaces.renderkit.html.util.AddResource2;
41  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
42  import org.apache.myfaces.tomahawk.util.ExternalContextUtils;
43  import org.apache.myfaces.webapp.filter.portlet.PortletExternalContextWrapper;
44  import org.apache.myfaces.webapp.filter.servlet.ServletExternalContextWrapper;
45  
46  /**
47   * This class acts as an alternative to ExtensionsFilter feature.
48   * <p>
49   * It wraps the FacesContext using TomahawkFacesContextFactory. See this
50   * class for parameters and additional information
51   * </p>
52   * <p>
53   * If ExtensionsFilter is used on servlet environment, this wrapper is 
54   * not used. You can set this wrapper using the following configuration:
55   * <code>
56   *   <context-param>
57   *     <param-name>org.apache.myfaces.CHECK_EXTENSIONS_FILTER</param-name>
58   *     <param-value>false</param-value>
59   *   </context-param>
60   * 
61   *   <context-param>
62   *     <param-name>org.apache.myfaces.DISABLE_TOMAHAWK_FACES_CONTEXT_WRAPPER</param-name>
63   *     <param-value>false</param-value>
64   *   </context-param>
65   * </code>
66   * </p>
67   * <p>
68   * Remember map the FacesServet to the org.apache.myfaces.RESOURCE_VIRTUAL_PATH
69   * value (default /faces/myFacesExtensionResource, so map FacesServlet to /faces/* 
70   * could be used or better /faces/myFacesExtensionResource/*) so the
71   * ServeResourcePhaseListener can serve resources.
72   * </p>
73   * <p>
74   * One use that has this wrapper is in portlets (there is no PortletFilter
75   * on portlet api 1.0, so to take all tomahawk advantages (components
76   * that uses some javascript handled by AddResource api and fileupload
77   * support) users must configure this alternative.
78   * </p>
79   * <p>
80   * When it is used this alternative, the params used to configure
81   * MultipartRequestWrapper (file upload support) are set using this
82   * web.xml config params: 
83   * </p>
84   * <ul>
85   * <li>org.apache.myfaces.UPLOAD_MAX_FILE_SIZE</li>
86   * <li>org.apache.myfaces.UPLOAD_THRESHOLD_SIZE</li>
87   * <li>org.apache.myfaces.UPLOAD_MAX_REPOSITORY_PATH</li>
88   * <li>org.apache.myfaces.UPLOAD_MAX_SIZE</li>
89   * <li>org.apache.myfaces.UPLOAD_CACHE_FILE_SIZE_ERRORS</li>
90   * </ul>
91   * 
92   * @since 1.1.7
93   * @author Martin Marinschek
94   */
95  public class TomahawkFacesContextWrapper extends FacesContext {
96  
97      private FacesContext delegate=null;
98      private ExternalContext externalContextDelegate=null;
99      private ExtensionsResponseWrapper extensionsResponseWrapper = null;
100 
101     public TomahawkFacesContextWrapper(FacesContext delegate)
102     {
103         this(delegate, null);
104     }
105     
106     public TomahawkFacesContextWrapper(FacesContext delegate, 
107             ExtensionsResponseWrapper extensionsResponseWrapper ) {
108 
109         this.delegate = delegate;
110         
111         if(ExternalContextUtils.getRequestType(delegate.getExternalContext()).isPortlet()) {
112             
113             Object portletResponse = delegate.getExternalContext().getResponse();
114             Object portletRequest = delegate.getExternalContext().getRequest();
115 
116             Object extendedRequest = portletRequest;
117             Object extendedResponse = portletResponse;
118             
119             // For multipart/form-data requests we need to encapsulate
120             // the request using PortletMultipartRequestWrapper. This could not be
121             // done on TomahawkFacesContextFactory.getFacesContext(...)
122             // because we need an ExternalContext instance to get
123             // the init params for the ExtensionsFilter and initialize
124             // MultipartRequestWrapper with the correct values.
125 
126             boolean multipartContent = false;
127             
128             if (PortletUtils.isMultipartContent(portletRequest))
129             {
130                 multipartContent = true;
131                 MultipartRequestWrapperConfig config = MultipartRequestWrapperConfig
132                 .getMultipartRequestWrapperConfig(delegate
133                         .getExternalContext());
134 
135                 extendedRequest = new PortletMultipartRequestWrapper( portletRequest, config.getUploadMaxFileSize(),
136                         config.getUploadThresholdSize(), config.getUploadRepositoryPath(),
137                         config.getUploadMaxSize(), config.isCacheFileSizeErrors());
138             }
139             
140             AddResource addResource= AddResourceFactory.getInstance(this);
141             
142             if (addResource instanceof AddResource2)
143             {
144                 ((AddResource2)addResource).responseStarted(delegate.getExternalContext().getContext(), extendedRequest);
145             }
146             else
147             {
148                 addResource.responseStarted();
149             }
150 
151             if (addResource.requiresBuffer())
152             {
153                 throw new IllegalStateException("buffering not supported in the portal environment. "+
154                         " Use for org.apache.myfaces.ADD_RESOURCE_CLASS the value"+
155                         " org.apache.myfaces.renderkit.html.util.NonBufferingAddResource.");
156             }
157             
158             externalContextDelegate = new PortletExternalContextWrapper(
159                     delegate.getExternalContext(), extendedRequest, extendedResponse, multipartContent);
160         }
161         else {
162             HttpServletResponse httpResponse = (HttpServletResponse) delegate.getExternalContext().getResponse();
163             HttpServletRequest httpRequest = (HttpServletRequest) delegate.getExternalContext().getRequest();
164 
165             HttpServletRequest extendedRequest = httpRequest;
166 
167             // For multipart/form-data requests we need to encapsulate
168             // the request using MultipartRequestWrapper. This could not be
169             // done on TomahawkFacesContextFactory.getFacesContext(...)
170             // because we need an ExternalContext instance to get
171             // the init params for the ExtensionsFilter and initialize
172             // MultipartRequestWrapper with the correct values.
173             
174             boolean multipartContent = false;
175            
176             if (ServletFileUpload.isMultipartContent(httpRequest)) {
177                 multipartContent = true;
178                 
179                 MultipartRequestWrapperConfig config = MultipartRequestWrapperConfig
180                         .getMultipartRequestWrapperConfig(delegate
181                                 .getExternalContext());                
182                 
183                 extendedRequest = new MultipartRequestWrapper(httpRequest, config.getUploadMaxFileSize(),
184                         config.getUploadThresholdSize(), config.getUploadRepositoryPath(), 
185                         config.getUploadMaxSize(), config.isCacheFileSizeErrors());
186                 
187             }
188 
189             AddResource addResource= AddResourceFactory.getInstance(this);
190             
191             if (addResource instanceof AddResource2)
192             {
193                 ((AddResource2)addResource).responseStarted(delegate.getExternalContext().getContext(), extendedRequest);
194             }
195             else
196             {
197                 addResource.responseStarted();
198             }
199 
200             if (addResource.requiresBuffer() && extensionsResponseWrapper != null)
201             {
202                 //If the request requires buffer, this was already
203                 //wrapped (on TomahawkFacesContextFactory.getFacesContext(...) ),
204                 //but we need to save the wrapped response value 
205                 //on a local variable to then reference it on release() 
206                 //method and parse the old response.
207                 this.extensionsResponseWrapper = (ExtensionsResponseWrapper) extensionsResponseWrapper; 
208             }
209 
210             externalContextDelegate = new ServletExternalContextWrapper(
211                     delegate.getExternalContext(), extendedRequest, httpResponse, multipartContent);            
212         }
213     }
214     
215     /**
216      * This method uses reflection to call the method of the delegated
217      * FacesContext getELContext, present on 1.2. This should be done
218      * since we need compatibility between 1.1 and 1.2 for tomahawk
219      * 
220      * @return
221      */
222     public javax.el.ELContext getELContext() {
223         ;
224         try
225         {
226             return (javax.el.ELContext) delegate.getClass().getMethod("getELContext", null).invoke(delegate, null);
227         }
228         catch (IllegalArgumentException e)
229         {
230             //If the method is called with jsf 1.2, this should not 
231             //be thrown
232             throw new RuntimeException(e);
233         }
234         catch (SecurityException e)
235         {
236             //If the method is called with jsf 1.2, this should not 
237             //be thrown
238             throw new RuntimeException("JSF 1.2 method not implemented: "+e.getMessage());
239         }
240         catch (IllegalAccessException e)
241         {
242             //If the method is called with jsf 1.2, this should not 
243             //be thrown
244             throw new RuntimeException("JSF 1.2 method not implemented: "+e.getMessage());
245         }
246         catch (InvocationTargetException e)
247         {
248             //If the method is called with jsf 1.2, this should not 
249             //be thrown
250             throw new RuntimeException("JSF 1.2 method not implemented: "+e.getMessage());
251         }
252         catch (NoSuchMethodException e)
253         {
254             //If the method is called with jsf 1.2, this should not 
255             //be thrown
256             throw new RuntimeException("JSF 1.2 method not implemented: "+e.getMessage());            
257         }
258     }
259 
260     public Application getApplication() {
261         return delegate.getApplication();
262     }
263 
264     public Iterator getClientIdsWithMessages() {
265         return delegate.getClientIdsWithMessages();
266     }
267 
268     public ExternalContext getExternalContext() {
269         return externalContextDelegate==null?delegate.getExternalContext():externalContextDelegate;
270     }
271 
272     public FacesMessage.Severity getMaximumSeverity() {
273         return delegate.getMaximumSeverity();
274     }
275 
276     public Iterator getMessages() {
277         return delegate.getMessages();
278     }
279 
280     public Iterator getMessages(String clientId) {
281         return delegate.getMessages(clientId);
282     }
283 
284     public RenderKit getRenderKit() {
285         return delegate.getRenderKit();
286     }
287 
288     public boolean getRenderResponse() {
289         return delegate.getRenderResponse();
290     }
291 
292     public boolean getResponseComplete() {
293         return delegate.getResponseComplete();
294     }
295 
296     public ResponseStream getResponseStream() {
297         return delegate.getResponseStream();
298     }
299 
300     public void setResponseStream(ResponseStream responseStream) {
301         delegate.setResponseStream(responseStream);
302     }
303 
304     public ResponseWriter getResponseWriter() {
305         return delegate.getResponseWriter();
306     }
307 
308     public void setResponseWriter(ResponseWriter responseWriter) {
309         delegate.setResponseWriter(responseWriter);
310     }
311 
312     public UIViewRoot getViewRoot() {
313         return delegate.getViewRoot();
314     }
315 
316     public void setViewRoot(UIViewRoot root) {
317         delegate.setViewRoot(root);
318     }
319 
320     public void addMessage(String clientId, FacesMessage message) {
321         delegate.addMessage(clientId, message);
322     }
323 
324     public void release() {
325 
326         AddResource addResource=null;
327 
328         try
329         {
330             addResource= AddResourceFactory.getInstance(this);
331             if (addResource.requiresBuffer())
332             {
333                 if(extensionsResponseWrapper == null) {
334                     throw new NullPointerException("When wrapping the faces-context, add-resource told us that no buffer is required, " +
335                             "now it is required, and we have a null-extensionsResponseWrapper. Please fix add-resource to be consistent over a single request.");
336                 }
337                 extensionsResponseWrapper.finishResponse();
338 
339                 // write the javascript stuff for myfaces and headerInfo, if needed
340                 HttpServletResponse servletResponse = extensionsResponseWrapper.getDelegate();
341                 HttpServletRequest servletRequest = (HttpServletRequest) getExternalContext().getRequest();
342 
343                 String contentType = extensionsResponseWrapper.getContentType();
344                 
345                 // only parse HTML responses
346                 if (contentType != null && isValidContentType(contentType))
347                 {
348                     String oldResponse = extensionsResponseWrapper.toString();
349                     addResource.parseResponse(servletRequest, extensionsResponseWrapper.toString(),
350                             servletResponse);
351 
352                     addResource.writeMyFacesJavascriptBeforeBodyEnd(servletRequest,
353                             servletResponse);
354 
355                     if( ! addResource.hasHeaderBeginInfos() ){
356                         // writes the response if no header info is needed
357                         addResource.writeResponse(servletRequest, servletResponse);
358                         return;
359                     }
360 
361                     // Some headerInfo has to be added
362                     addResource.writeWithFullHeader(servletRequest, servletResponse);
363 
364                     // writes the response
365                     addResource.writeResponse(servletRequest, servletResponse);
366                 }
367                 else
368                 {
369 
370                     byte[] responseArray = extensionsResponseWrapper.getBytes();
371 
372                     if(responseArray.length > 0)
373                     {
374                          // When not filtering due to not valid content-type, deliver the byte-array instead of a charset-converted string.
375                          // Otherwise a binary stream gets corrupted.
376                          servletResponse.getOutputStream().write(responseArray);
377                      }
378                 }
379             }
380         }
381         catch(Throwable th) {
382             throw new FacesException(th);
383         }
384         finally
385         {
386             try
387             {
388                 if(addResource!=null)
389                 {
390                     addResource.responseFinished();
391                 }
392             }
393             finally
394             {
395                 delegate.release();
396             }
397         }
398 
399     }
400 
401     public boolean isValidContentType(String contentType)
402     {
403         return contentType.startsWith("text/html") ||
404                 contentType.startsWith("text/xml") ||
405                 contentType.startsWith("application/xhtml+xml") ||
406                 contentType.startsWith("application/xml");
407     }
408 
409     public void renderResponse() {
410         delegate.renderResponse();
411     }
412 
413     public void responseComplete() {
414         delegate.responseComplete();
415     }
416     
417     /*This method is for do not break MyfacesGenericPortlet*/
418     public void setExternalContext(ExternalContext extContext){
419         try
420         {
421             Method method = delegate.getClass().getMethod("setExternalContext", new Class[]{Class.forName("org.apache.myfaces.context.ReleaseableExternalContext")});
422             method.invoke(delegate, new Object[]{extContext});
423             FacesContext.setCurrentInstance(this);
424         }
425         catch (NoSuchMethodException e)
426         {
427             throw new RuntimeException("JSF 1.2 method not implemented: "+e.getMessage());
428         }
429         catch (Exception e)
430         {
431             throw new RuntimeException("Error calling JSF 1.2 method: "+e.getMessage());
432         }
433     }
434     
435 }