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.custom.fileupload;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.ProjectStage;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.behavior.ClientBehavior;
29  import javax.faces.component.behavior.ClientBehaviorHolder;
30  import javax.faces.context.ExternalContext;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  import javax.faces.convert.ConverterException;
34  import javax.servlet.ServletRequest;
35  import javax.servlet.http.HttpServletRequestWrapper;
36  
37  import org.apache.commons.fileupload.FileItem;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.myfaces.component.UserRoleUtils;
41  import org.apache.myfaces.shared_tomahawk.util.WebConfigParamUtils;
42  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
43  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
44  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
45  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
46  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.ResourceUtils;
47  import org.apache.myfaces.tomahawk.util.ExternalContextUtils;
48  import org.apache.myfaces.webapp.filter.ExtensionsFilter;
49  import org.apache.myfaces.webapp.filter.MultipartRequestWrapper;
50  import org.apache.myfaces.webapp.filter.TomahawkFacesContextFactory;
51  
52  /**
53   * Renderer for the HtmlInputFileUpload component.
54   * <p>
55   * See also class AbstractHtmlInputFileUpload.
56   * 
57   * @JSFRenderer
58   *   renderKitId = "HTML_BASIC" 
59   *   family = "javax.faces.Input"
60   *   type = "org.apache.myfaces.FileUpload"
61   * 
62   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
63   * @version $Revision: 782515 $ $Date: 2009-06-07 22:28:42 -0500 (dom, 07 jun 2009) $
64   */
65  public class HtmlFileUploadRenderer
66          extends HtmlRenderer
67  {
68      private static final Log log = LogFactory.getLog(HtmlFileUploadRenderer.class);
69      
70      private static boolean getBooleanInitParameterValue(String initParameter, boolean defaultVal)
71      {
72          if(initParameter == null || initParameter.trim().length()==0)
73              return defaultVal;
74  
75          return (initParameter.equalsIgnoreCase("on") || initParameter.equals("1") || initParameter.equalsIgnoreCase("true"));
76      }  
77      
78      public void encodeEnd(FacesContext facesContext, UIComponent uiComponent)
79              throws IOException
80      {
81          super.encodeEnd(facesContext, uiComponent); //check for NP
82  
83          //Check the current setup has ExtensionsFilter or TomahawkFacesContextWrapper
84          boolean isDisabledTomahawkFacesContextWrapper = getBooleanInitParameterValue(
85                  WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
86                          TomahawkFacesContextFactory.DISABLE_TOMAHAWK_FACES_CONTEXT_WRAPPER), 
87                          TomahawkFacesContextFactory.DISABLE_TOMAHAWK_FACES_CONTEXT_WRAPPER_DEFAULT);
88  
89          if (!facesContext.isProjectStage(ProjectStage.Production))
90          {
91              boolean isPortlet = ExternalContextUtils.getRequestType(facesContext.getExternalContext()).isPortlet();
92              //if it is a portlet request and no TomahawkFacesContextWrapper set or
93              //is servlet and no ExtensionsFilter/TomahawkFacesContextWrapper set
94              //log a warning, because something is not right
95              boolean requestInterceptedByFilter = facesContext.getExternalContext().getRequestMap().containsKey(ExtensionsFilter.DOFILTER_CALLED);
96              boolean extensionsFilterInitialized = facesContext.getExternalContext().getApplicationMap().containsKey(ExtensionsFilter.EXTENSIONS_FILTER_INITIALIZED); 
97              if ( (isPortlet && isDisabledTomahawkFacesContextWrapper) ||
98                   (isDisabledTomahawkFacesContextWrapper &&
99                    !(requestInterceptedByFilter && extensionsFilterInitialized)))
100             {
101                 if (log.isWarnEnabled())
102                 {
103                     log.warn("t:inputFileUpload requires ExtensionsFilter or TomahawkFacesContextFactory configured to handle multipart file upload, and any of " +
104                             "them has been detected. Please read the instructions on http://myfaces.apache.org/tomahawk/extensionsFilter.html " +
105                             "for more information about how to setup your environment correctly. Please be sure ExtensionsFilter is the top most " +
106                             "filter if you are using multiple jsf libraries.");
107                     if (extensionsFilterInitialized && !requestInterceptedByFilter)
108                     {
109                         log.warn("t:inputFileUpload requires the current request be intercepted by the filter in order to handle forms with multipart encode type. " +
110                                 "ExtensionsFilter was initialized but the current request was not intercepted. " +
111                                 "Please add a filter-mapping entry to intercept jsf page requests.");
112                     }
113                 }
114             }
115         }
116 
117         ResponseWriter writer = facesContext.getResponseWriter();
118         
119         Map<String, List<ClientBehavior>> behaviors = null;
120         if (uiComponent instanceof ClientBehaviorHolder)
121         {
122             behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
123             if (!behaviors.isEmpty())
124             {
125                 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
126             }
127         }
128         
129         writer.startElement(HTML.INPUT_ELEM, uiComponent);
130         writer.writeAttribute(HTML.TYPE_ATTR, HTML.FILE_ATTR, null);
131         String clientId = uiComponent.getClientId(facesContext);
132         if (behaviors != null && !behaviors.isEmpty())
133         {
134             writer.writeAttribute(HTML.ID_ATTR, clientId,null);
135         }
136         else
137         {
138             renderId(facesContext, uiComponent);
139         }
140         writer.writeAttribute(HTML.NAME_ATTR, clientId, null);
141         UploadedFile value = (UploadedFile)((HtmlInputFileUpload)uiComponent).getValue();
142         if (value != null)
143         {
144             if( value.getName() != null )
145             {
146                 writer.writeAttribute(HTML.VALUE_ATTR, value.getName(), null);
147             }
148         }
149         
150         if (behaviors != null && !behaviors.isEmpty())
151         {
152             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.INPUT_FILE_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED_AND_EVENTS);
153             HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
154             HtmlRendererUtils.renderBehaviorizedFieldEventHandlersWithoutOnchange(facesContext, writer, uiComponent, behaviors);
155             HtmlRendererUtils.renderBehaviorizedOnchangeEventHandler(facesContext, writer, uiComponent, behaviors);
156         }
157         else
158         {
159             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.INPUT_FILE_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
160         }
161         
162         if (isDisabled(facesContext, uiComponent))
163         {
164             writer.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
165         }
166 
167         writer.endElement(HTML.INPUT_ELEM);
168     }
169 
170     protected boolean isDisabled(FacesContext facesContext, UIComponent uiComponent)
171     {
172         if (!UserRoleUtils.isEnabledOnUserRole(uiComponent))
173         {
174             //if the user is not enabled, the component is
175             //disabled, so it should return true.
176             return true;
177         }
178         else
179         {
180             if (uiComponent instanceof HtmlInputFileUpload)
181             {
182                 return ((HtmlInputFileUpload)uiComponent).isDisabled();
183             }
184             else
185             {
186                 return RendererUtils.getBooleanAttribute(uiComponent, HTML.DISABLED_ATTR, false);
187             }
188         }
189     }
190 
191     private void setSubmittedValueForImplementation(
192         FacesContext facesContext, UIComponent uiComponent, FileItem fileItem)
193     {
194         try
195         {
196             UploadedFile upFile;
197             String implementation =
198                 ((HtmlInputFileUpload)uiComponent).getStorage();
199             if (implementation == null || implementation.length() == 0)
200             {
201                 implementation = "default";
202             }
203             if (("memory").equals(implementation))
204             {
205                 upFile = new UploadedFileDefaultMemoryImpl(fileItem);
206             }
207             else if (("default").equals(implementation))
208             {
209                 if (fileItem.isInMemory())
210                 {
211                     upFile = new UploadedFileDefaultMemoryImpl(fileItem);
212                 }
213                 else
214                 {
215                     upFile = new UploadedFileDefaultFileImpl(fileItem);                    
216                 }
217             }
218             else //"file" case
219             {
220                 upFile = new UploadedFileDefaultFileImpl(fileItem);
221             }
222 
223             ((HtmlInputFileUpload)uiComponent).setSubmittedValue(upFile);
224             ((HtmlInputFileUpload)uiComponent).setValid(true);
225         }
226         catch (IOException ioe)
227         {
228             throw new FacesException(
229                 "Exception while processing file upload for file-input : "
230                     + uiComponent.getClientId(facesContext), ioe);
231         }
232     }
233 
234     /**
235      * Handle the postback of a form containing a fileUpload component.
236      * <p>
237      * The browser request will have been in "multi-part-mime" format, where
238      * the normal http post is in one part, and the file being uploaded is
239      * in another. Hopefully JSF has been configured so that this special
240      * request is wrapped in a custom ServletRequest that allows us to
241      * fetch that extra data....
242      */
243     public void decode(FacesContext facesContext, UIComponent uiComponent)
244     {
245         super.decode(facesContext, uiComponent); //check for NP
246 
247         HtmlRendererUtils.decodeClientBehaviors(facesContext, uiComponent);
248         //MultipartWrapper might have been wrapped again by one or more additional
249         //Filters. We try to find the MultipartWrapper, but if a filter has wrapped
250         //the ServletRequest with a class other than HttpServletRequestWrapper
251         //this will fail.
252         Object request = facesContext.getExternalContext().getRequest();
253         if (!(request instanceof ServletRequest)) {
254             ExternalContext externalContext = facesContext.getExternalContext();
255             Map fileItems = (Map)externalContext.getRequestMap().
256                     get(MultipartRequestWrapper.UPLOADED_FILES_ATTRIBUTE);
257             FileItem fileItem = null;
258             if (fileItems != null) {
259                 String paramName = uiComponent.getClientId(facesContext);
260                 fileItem = (FileItem) fileItems.get(paramName);
261             }
262             if (fileItem != null)
263             {
264                 setSubmittedValueForImplementation(facesContext, uiComponent, fileItem);
265             }
266             return;
267         }
268         if(facesContext.getExternalContext().getRequest() instanceof ServletRequest)
269         {
270             ServletRequest multipartRequest = (ServletRequest)facesContext.getExternalContext().getRequest();
271             while (multipartRequest != null &&
272                     !(multipartRequest instanceof MultipartRequestWrapper))
273             {
274                 if (multipartRequest instanceof HttpServletRequestWrapper)
275                 {
276                     multipartRequest = ((HttpServletRequestWrapper)multipartRequest).getRequest();
277                 }
278                 else
279                 {
280                     multipartRequest = null;
281                 }
282             }
283 
284             if (multipartRequest != null)
285             {
286                 MultipartRequestWrapper mpReq = (MultipartRequestWrapper)multipartRequest;
287 
288                 String paramName = uiComponent.getClientId(facesContext);
289                 FileItem fileItem = mpReq.getFileItem(paramName);
290                 if (fileItem != null)
291                 {
292                     setSubmittedValueForImplementation(facesContext, uiComponent, fileItem);
293                 }
294             }
295         }
296     }
297 
298     public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException
299     {
300         if(submittedValue instanceof UploadedFile)
301         {
302             UploadedFile file = (UploadedFile) submittedValue;
303 
304             if(file.getSize()>0 && file.getName()!=null && file.getName().length()>0)
305             {
306                 return file;
307             }
308         }
309 
310         return null;
311     }
312 }