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  
20  package org.apache.myfaces.renderkit.html.util;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.myfaces.shared_tomahawk.config.MyfacesConfig;
25  import org.apache.myfaces.shared_tomahawk.util.ClassUtils;
26  import org.apache.myfaces.shared_tomahawk.webapp.webxml.FilterMapping;
27  import org.apache.myfaces.shared_tomahawk.webapp.webxml.WebXml;
28  import org.apache.myfaces.tomahawk.util.ExternalContextUtils;
29  import org.apache.myfaces.webapp.filter.ExtensionsFilter;
30  
31  import javax.faces.context.ExternalContext;
32  import javax.faces.context.FacesContext;
33  import javax.portlet.PortletContext;
34  import javax.portlet.PortletResponse;
35  import javax.servlet.ServletContext;
36  import javax.servlet.http.HttpServletRequest;
37  import java.util.*;
38  
39  /**
40   * This class provides the ability to instantiate AddResource objects. By
41   * default, this class will instantiate instances of
42   * org.apache.myfaces.renderkit.html.util.DefaultAddResource. However, the
43   * context parameter org.apache.myfaces.ADD_RESOURCE_CLASS can specify an
44   * alternative implementation of the AddResource interface. The class must have
45   * a constructor with a single String argument, representing the context path.
46   * <p/> Mostly used to avoid having to include [script src="..."][/script] in
47   * the head of the pages before using a component.
48   * 
49   * @author Peter Mahoney
50   * @author Sylvain Vieujot (latest modification by $Author: mmarinschek $)
51   * @version $Revision: 358042 $ $Date: 2005-12-20 17:12:56 +0000 (Tue, 20 Dec
52   *          2005) $
53   */
54  public class AddResourceFactory
55  {
56      public static class RequestMapWrapper implements Map
57      {
58          private final HttpServletRequest request;
59  
60          public RequestMapWrapper(final HttpServletRequest request)
61          {
62              this.request = request;
63          }
64  
65          public int size()
66          {
67              throw new UnsupportedOperationException();
68          }
69  
70          public boolean isEmpty()
71          {
72              throw new UnsupportedOperationException();
73          }
74  
75          public boolean containsKey(Object key)
76          {
77              if (key == null)
78              {
79                  throw new UnsupportedOperationException(
80                      "'null' key not supported");
81              }
82              return request.getAttribute(key.toString()) != null;
83          }
84  
85          public boolean containsValue(Object value)
86          {
87              throw new UnsupportedOperationException();
88          }
89  
90          public Object get(Object key)
91          {
92              if (key == null)
93              {
94                  throw new UnsupportedOperationException(
95                      "'null' key not supported");
96              }
97              return request.getAttribute(key.toString());
98          }
99  
100         public Object put(Object key, Object value)
101         {
102             if (key == null)
103             {
104                 throw new UnsupportedOperationException(
105                     "'null' key not supported");
106             }
107 
108             Object old = request.getAttribute(key.toString());
109             request.setAttribute(key.toString(), value);
110             return old;
111         }
112 
113         public Object remove(Object key)
114         {
115             if (key == null)
116             {
117                 throw new UnsupportedOperationException(
118                     "'null' key not supported");
119             }
120 
121             Object old = request.getAttribute(key.toString());
122             request.removeAttribute(key.toString());
123             return old;
124         }
125 
126         public void putAll(Map arg)
127         {
128             throw new UnsupportedOperationException();
129         }
130 
131         public void clear()
132         {
133             throw new UnsupportedOperationException();
134         }
135 
136         public Set keySet()
137         {
138             throw new UnsupportedOperationException();
139         }
140 
141         public Collection values()
142         {
143             throw new UnsupportedOperationException();
144         }
145 
146         public Set entrySet()
147         {
148             throw new UnsupportedOperationException();
149         }
150 
151     }
152 
153     protected static final Log log = LogFactory
154         .getLog(AddResourceFactory.class);
155 
156     private final static String CACHE_MAP_KEY = "org.apache.myfaces.AddResourceFactory.CACHE_MAP_KEY";
157     private final static String ENV_CHECKED_KEY = "org.apache.myfaces.AddResourceFactory.ENV_CHECKED_KEY";
158 
159     /**
160      * Internal factory method. <p/> Return an instance of AddResource keyed by
161      * context path, or create one if no such instance already exists. The
162      * instance will be cached using the given Map, most likely this will the
163      * the request map of your servlet request. Therefore every request uses its
164      * own AddResource instance.
165      * </p>
166      * <p/> Note that this method is package-scope for the purposes of
167      * unit-testing only. This method should be treated as private by non-test
168      * code.
169      * </p>
170      * 
171      * @param cacheMap
172      *            the map used for caching of the instance. if null, a new
173      *            instance will be created all the time (for tests)
174      * @param contextPath
175      *            context path of your web-app
176      * @param addResourceClassName
177      *            class name of a class implementing the
178      * @link AddResource interface
179      */
180     static AddResource getInstance(Object context, Map cacheMap, String contextPath,
181             String addResourceClassName)
182     {
183         AddResource instance = null;
184 
185         if (cacheMap != null)
186         {
187             instance = (AddResource) cacheMap.get(CACHE_MAP_KEY);
188         }
189 
190         if (instance == null)
191         {
192             if (addResourceClassName == null)
193             {
194                 // For efficiency don't use reflection unless it is necessary
195                 if(context instanceof PortletContext) {
196                     instance = new NonBufferingAddResource();
197                     instance.setContextPath(contextPath);
198                 }
199                 else {
200                     instance = new DefaultAddResource();
201                     instance.setContextPath(contextPath);
202                 }
203             }
204             else
205             {
206                 try
207                 {
208                     Class addResourceClass = ClassUtils
209                         .classForName(addResourceClassName);
210 
211                     if (AddResource.class.isAssignableFrom(addResourceClass))
212                     {
213                         AddResource tmpInstance = (AddResource) addResourceClass
214                             .newInstance();
215                         tmpInstance.setContextPath(contextPath);
216 
217                         // only use a fully initialized instance
218                         instance = tmpInstance;
219                     }
220                     else
221                     {
222                         log
223                             .error("Invalid AddResource class ("
224                                     + addResourceClass.getName()
225                                     + "). Must implement the AddResource interface.");
226                     }
227                 }
228                 catch (ClassNotFoundException e)
229                 {
230                     log
231                         .error(
232                             "AddResource class not found. Using default class instead",
233                             e);
234                 }
235                 catch (IllegalArgumentException e)
236                 {
237                     // This should not happen as the constructor has been
238                     // checked
239                     log.error(e);
240                 }
241                 catch (InstantiationException e)
242                 {
243                     log.error(
244                         "Invalid AddResource class. Must be non-abstract", e);
245                 }
246                 catch (IllegalAccessException e)
247                 {
248                     log.error("Could not access AddResource class", e);
249                 }
250                 finally
251                 {
252                     // Ensure there is always an AddResource object available
253                     if (instance == null)
254                     {
255                         instance = new DefaultAddResource();
256                         instance.setContextPath(contextPath);
257                     }
258                 }
259             }
260 
261             if (cacheMap != null)
262             {
263                 cacheMap.put(CACHE_MAP_KEY, instance);
264             }
265         }
266 
267         return instance;
268     }
269 
270     public static AddResource getInstance(FacesContext context)
271     {
272         ExternalContext externalContext = context
273             .getExternalContext();
274 
275         AddResource addResource = getInstance(externalContext.getContext(), externalContext.getRequestMap(),
276                 externalContext.getRequestContextPath(), MyfacesConfig
277             .getCurrentInstance(externalContext)
278             .getAddResourceClass());
279         checkEnvironment(context, addResource);
280         return addResource;
281     }
282 
283     /**
284      * @deprecated use getInstance(HttpServletRequest request, ServletContext servletContext) instead
285      */
286     public static AddResource getInstance(HttpServletRequest request)
287     {
288         ServletContext servletContext = request
289             .getSession().getServletContext();
290         Map requestMap = new RequestMapWrapper(request);
291         AddResource addResource = getInstance(servletContext, requestMap, request
292             .getContextPath(), MyfacesConfig
293             .getAddResourceClassFromServletContext(servletContext));
294         //
295         // this will be called by the ExtensionsFilter itself, so no need to
296         // check the environment
297         //
298         return addResource;
299     }
300     
301     public static AddResource getInstance(HttpServletRequest request, ServletContext servletContext)
302     {
303         Map requestMap = new RequestMapWrapper(request);
304         AddResource addResource = getInstance(servletContext, requestMap, request
305             .getContextPath(), MyfacesConfig
306             .getAddResourceClassFromServletContext(servletContext));
307         //
308         // this will be called by the ExtensionsFilter itself, so no need to
309         // check the environment
310         //
311         return addResource;        
312     }
313 
314     /**
315      * check if the extensionsFilter has been correctly setup.
316      */
317     private static void checkEnvironment(FacesContext context, AddResource addResource)
318     {
319         ExternalContext extctx = context.getExternalContext();
320 
321         //a check for a servlet-filter only makes sense in a servlet-environment....
322         //attention: checking for servlet-response doesn't work here - quite often, the portlet-response
323         //is implemented as a servlet-response, and therefore the check is implemented inversely
324         
325         //if(extctx.getResponse() instanceof PortletResponse) {        
326         if(ExternalContextUtils.getRequestType(extctx).isPortlet()) {
327             return;
328         }
329 
330         if (extctx.getApplicationMap().containsKey(ENV_CHECKED_KEY))
331         {
332             // already checked
333             return;
334         }
335 
336         if (!MyfacesConfig.getCurrentInstance(extctx).isCheckExtensionsFilter())
337         {
338             // checks disabled by user request
339             extctx.getApplicationMap().put(ENV_CHECKED_KEY, Boolean.TRUE);
340             return;
341         }
342 
343         synchronized (extctx.getApplicationMap())
344         {
345             if (addResource.requiresBuffer())
346             {
347                 if (!extctx.getRequestMap().containsKey(ExtensionsFilter.DOFILTER_CALLED))
348                 {
349                     throwExtensionsFilterMissing("JSF mapping missing. JSF pages not covered.");
350                 }
351             }
352 
353             boolean foundMapping = false;
354 
355             List facesServletMappings = WebXml.getWebXml(extctx).getFacesExtensionsFilterMappings();
356             for (Iterator iterServletMappings = facesServletMappings.iterator(); iterServletMappings.hasNext();)
357             {
358                 FilterMapping filterMapping = (FilterMapping) iterServletMappings.next();
359                 if (checkFilterPattern(extctx, filterMapping))
360                 {
361                     foundMapping = true;
362                     break;
363                 }
364             }
365 
366             if (!foundMapping)
367             {
368                 throwExtensionsFilterMissing("Resource mapping missing. Resources cant be delivered.");
369             }
370 
371             extctx.getApplicationMap().put(ENV_CHECKED_KEY, Boolean.TRUE);
372         }
373     }
374 
375     protected static boolean checkFilterPattern(ExternalContext extCtxt, FilterMapping filterMapping)
376     {
377         if (filterMapping.getUrlPattern() != null &&
378                 ("/faces/*".equals(filterMapping.getUrlPattern()) ||
379                         (MyfacesConfig.getCurrentInstance(extCtxt).getResourceVirtualPath()+"/*").equals(filterMapping.getUrlPattern())))
380         {
381             return true;
382         }
383         
384         return false;
385     }
386 
387     private static void throwExtensionsFilterMissing(String detailReason)
388     {
389         throw new IllegalStateException(
390             "ExtensionsFilter not correctly configured. " + detailReason + " Please see: http://myfaces.apache.org/tomahawk/extensionsFilter.html");
391     }
392 }