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.shared.application;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.faces.context.ExternalContext;
28  import javax.faces.context.FacesContext;
29  import javax.faces.webapp.FacesServlet;
30  import javax.servlet.ServletContext;
31  import javax.servlet.ServletRegistration;
32  import org.apache.myfaces.shared.config.MyfacesConfig;
33  import org.apache.myfaces.shared.util.ClassUtils;
34  import org.apache.myfaces.shared.util.ExternalContextUtils;
35  import org.apache.myfaces.shared.webapp.webxml.DelegatedFacesServlet;
36  
37  public class FacesServletMappingUtils
38  {
39      private static final String FACES_SERVLET_REGISTRATION = "org.apache.myfaces.FACES_SERVLET_REGISTRATION";
40      private static final String SERVLET_REGISTRATIONS = "org.apache.myfaces.SERVLET_REGISTRATIONS";
41      
42      private static final String CURRENT_REQUEST_FACES_SERVLET = "org.apache.myfaces.CURRENT_FACES_SERVLET_MAPPING";
43      
44      /**
45       * Wrapper for better performance
46       */
47      public static class ServletRegistrationInfo
48      {
49          private String className;
50          private String[] mappings;
51          private boolean facesServlet;
52          private ServletRegistration registration;
53  
54          public ServletRegistrationInfo(ServletRegistration registration, boolean facesServlet)
55          {
56              this.className = registration.getClassName();
57              this.facesServlet = facesServlet;
58              this.registration = registration;
59  
60              Collection<String> mappingsCollection = registration.getMappings();
61              mappings = mappingsCollection.toArray(new String[mappingsCollection.size()]);
62              if (mappings == null)
63              {
64                  mappings = new String[]{ };
65              }
66          }
67  
68          public String getClassName()
69          {
70              return className;
71          }
72  
73          public String[] getMappings()
74          {
75              return mappings;
76          }
77  
78          public boolean isFacesServlet()
79          {
80              return facesServlet;
81          }
82          
83          public ServletRegistration getRegistration()
84          {
85              return registration;
86          }
87      }
88      
89      public static FacesServletMapping getCurrentRequestFacesServletMapping(FacesContext context)
90      {
91          Map<Object, Object> attributes = context.getAttributes();
92  
93          // Has the mapping already been determined during this request?
94          FacesServletMapping mapping = (FacesServletMapping) attributes.get(CURRENT_REQUEST_FACES_SERVLET);
95          if (mapping == null)
96          {
97              ExternalContext externalContext = context.getExternalContext();
98              mapping = calculateFacesServletMapping(
99                      context,
100                     externalContext.getRequestServletPath(),
101                     externalContext.getRequestPathInfo(),
102                     true);
103 
104             attributes.put(CURRENT_REQUEST_FACES_SERVLET, mapping);
105         }
106         return mapping;
107     }
108 
109     public static List<ServletRegistrationInfo> getServletRegistrations(FacesContext facesContext,
110             ServletContext servletContext, boolean cache)
111     {
112         Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
113         
114         List<ServletRegistrationInfo> infos =
115                 (List<ServletRegistrationInfo>) applicationMap.get(SERVLET_REGISTRATIONS);
116         if (infos == null)
117         {
118             infos = new ArrayList<>();
119             
120             Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
121             if (registrations != null)
122             {
123                 for (ServletRegistration servletRegistration : registrations.values())
124                 {
125                     ServletRegistrationInfo info = new ServletRegistrationInfo(servletRegistration,
126                             isFacesServlet(facesContext, servletRegistration.getClassName()));
127 
128                     infos.add(info);
129                 }
130             }
131             
132             infos = Collections.unmodifiableList(infos);
133             if (cache)
134             {
135                 applicationMap.put(SERVLET_REGISTRATIONS, infos);
136             }
137         }
138 
139         return infos;
140     }
141     
142     public static ServletRegistrationInfo getFacesServletRegistration(FacesContext facesContext,
143             ServletContext servletContext, boolean cache)
144     {
145         Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
146         
147         ServletRegistrationInfo facesServletRegistration = (ServletRegistrationInfo)
148                 applicationMap.get(FACES_SERVLET_REGISTRATION);
149         if (facesServletRegistration == null)
150         {
151             for (ServletRegistrationInfo info : getServletRegistrations(facesContext, servletContext, cache))
152             {
153                 if (info.isFacesServlet())
154                 {
155                     facesServletRegistration = info;
156                     break;
157                 }
158             }
159 
160             if (facesServletRegistration != null && cache)
161             {
162                 applicationMap.put(FACES_SERVLET_REGISTRATION, facesServletRegistration);
163             }
164         }
165 
166         return facesServletRegistration;
167     }
168 
169     public static boolean isFacesServlet(FacesContext facesContext, String servletClassName)
170     {
171         // shortcut to avoid class lookup
172         if (FacesServlet.class.getName().equals(servletClassName))
173         {
174             return true;
175         }
176 
177         Class servletClass = ClassUtils.simpleClassForName(servletClassName, false);
178         if (servletClass != null)
179         {
180             MyfacesConfig config = MyfacesConfig.getCurrentInstance(facesContext.getExternalContext());
181             
182             return FacesServlet.class.isAssignableFrom(servletClass)
183                     || DelegatedFacesServlet.class.isAssignableFrom(servletClass)
184                     || servletClass.getName().equals(config.getDelegateFacesServlet());
185         }
186         return false;
187     }
188     
189     
190     
191     
192 
193     public static FacesServletMapping calculateFacesServletMapping(
194         FacesContext facesContext, String servletPath, String pathInfo, boolean allowExactMapping)
195     {
196         if (ExternalContextUtils.isPortlet(facesContext.getExternalContext()))
197         {
198             return calculateFacesServletMapping(servletPath, pathInfo);
199         }
200         else
201         {
202             Object context = facesContext.getExternalContext().getContext();
203             if (context instanceof ServletContext)
204             {
205                 if (pathInfo != null)
206                 {
207                     // If there is a "extra path", it's definitely no extension mapping.
208                     // Now we just have to determine the path which has been specified
209                     // in the url-pattern, but that's easy as it's the same as the
210                     // current servletPath. It doesn't even matter if "/*" has been used
211                     // as in this case the servletPath is just an empty string according
212                     // to the Servlet Specification (SRV 4.4).
213                     return createMappingFromServletRegistration(facesContext, 
214                             (ServletContext)context, servletPath, pathInfo, allowExactMapping);
215                 }
216                 else
217                 {
218                     // In the case of extension mapping, no "extra path" is available.
219                     // Still it's possible that prefix-based mapping has been used.
220                     // Actually, if there was an exact match no "extra path"
221                     // is available (e.g. if the url-pattern is "/faces/*"
222                     // and the request-uri is "/context/faces").
223                     String extension = extractExtensionFromUrl(servletPath);
224                     if (extension != null)
225                     {
226                         return FacesServletMapping.createExtensionMapping(extension);
227                     }
228                     else
229                     {
230                         // There is no extension in the given servletPath and therefore
231                         // we assume that it's an exact match using prefix-based mapping.
232                         return createMappingFromServletRegistration(facesContext, 
233                                 (ServletContext)context, servletPath, pathInfo, allowExactMapping);
234                     }
235                 }
236             }
237             else
238             {
239                 return calculateFacesServletMapping(servletPath, pathInfo);
240             }
241         }
242     }
243     
244     private static FacesServletMapping createMappingFromServletRegistration(FacesContext facesContext, 
245             ServletContext servletContext, String servletPath, String pathInfo, boolean allowExactMatch)
246     {
247         try
248         {
249             List<ServletRegistrationInfo> servletRegistrations =
250                     getServletRegistrations(facesContext, servletContext, true);
251             if (servletRegistrations  != null)
252             {
253                 FacesServletMapping facesExtensionMapping = null;
254                 FacesServletMapping facesPrefixMapping = null;
255                 FacesServletMapping facesExactMapping = null;
256 
257                 for (ServletRegistrationInfo servletRegistration : servletRegistrations)
258                 {
259                     try
260                     {
261                         if (servletRegistration.isFacesServlet())
262                         {
263                             for (String mapping : servletRegistration.getMappings())
264                             {
265                                 if (isExtensionMapping(mapping))
266                                 {
267                                     facesExtensionMapping = FacesServletMapping.createExtensionMapping(
268                                             extractExtension(mapping));
269                                 }
270                                 else if (isPrefixMapping(mapping))
271                                 {
272                                     facesPrefixMapping = FacesServletMapping.createPrefixMapping(
273                                             extractPrefix(mapping));
274                                 }
275                                 else if (allowExactMatch && mapping.startsWith("/") && mapping.equals(servletPath))
276                                 {
277                                     facesExactMapping = FacesServletMapping.createExactMapping(servletPath);
278                                 }
279                             }
280                         }
281                         else
282                         {
283                             //This is not a FacesServlet mapping. 
284                             //It could be a non-faces request, we need to look for exact mapping to servletPath
285                             //this happens with richfaces resources
286                             for (String mapping : servletRegistration.getMappings())
287                             {                                
288                                 if (mapping.startsWith("/") && mapping.endsWith("/*"))
289                                 {
290                                     mapping = mapping.substring(0, mapping.length()-2);
291                                 }                                
292                                 if (mapping.equals(servletPath))
293                                 {
294                                     return FacesServletMapping.createPrefixMapping(mapping);
295                                 }
296                             }
297                        }
298                     }
299                     catch (Exception ex)
300                     {
301                         //No op
302                     }
303                 }
304 
305                 // Choose exact mapping if preferred.
306                 if (allowExactMatch && facesExactMapping != null)
307                 {
308                     return facesExactMapping;
309                 }
310                 else if (facesPrefixMapping != null)
311                 {
312                     return facesPrefixMapping;
313                 }
314                 else if (facesExtensionMapping != null)
315                 {
316                     return facesExtensionMapping;
317                 }
318                 else
319                 {
320                     return FacesServletMapping.createPrefixMapping(servletPath);
321                 }
322             }
323             else
324             {
325                 return FacesServletMapping.createPrefixMapping(servletPath);
326             }
327         }
328         catch(Exception ex)
329         {
330             return FacesServletMapping.createPrefixMapping(servletPath);
331         }
332     }
333 
334     /**
335      * Determines the mapping of the FacesServlet in the web.xml configuration
336      * file. However, there is no need to actually parse this configuration file
337      * as runtime information is sufficient.
338      *
339      * @param servletPath The servletPath of the current request
340      * @param pathInfo    The pathInfo of the current request
341      * @return the mapping of the FacesServlet in the web.xml configuration file
342      */
343     private static FacesServletMapping calculateFacesServletMapping(String servletPath, String pathInfo)
344     {
345         if (pathInfo != null)
346         {
347             // If there is a "extra path", it's definitely no extension mapping.
348             // Now we just have to determine the path which has been specified
349             // in the url-pattern, but that's easy as it's the same as the
350             // current servletPath. It doesn't even matter if "/*" has been used
351             // as in this case the servletPath is just an empty string according
352             // to the Servlet Specification (SRV 4.4).
353             return FacesServletMapping.createPrefixMapping(servletPath);
354         }
355         else
356         {
357             // In the case of extension mapping, no "extra path" is available.
358             // Still it's possible that prefix-based mapping has been used.
359             // Actually, if there was an exact match no "extra path"
360             // is available (e.g. if the url-pattern is "/faces/*"
361             // and the request-uri is "/context/faces").
362             String extension = extractExtensionFromUrl(servletPath);
363             if (extension != null)
364             {
365                 return FacesServletMapping.createExtensionMapping(extension);
366             }
367             else
368             {
369                 // There is no extension in the given servletPath and therefore
370                 // we assume that it's an exact match using prefix-based mapping.
371                 return FacesServletMapping.createExactMapping(servletPath);
372             }
373         }
374     }
375     
376     public static FacesServletMapping getExactMapping(FacesContext facesContext, String prefixedExactMappingViewId)
377     {
378         if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext()))
379         {
380             Object context = facesContext.getExternalContext().getContext();
381             if (context instanceof ServletContext)
382             {
383                 ServletRegistrationInfo facesServletRegistration =
384                         getFacesServletRegistration(facesContext, (ServletContext) context, true);
385                 if (facesServletRegistration != null)
386                 {
387                     for (String mapping : facesServletRegistration.getMappings())
388                     {
389                         if (!mapping.contains("*") && prefixedExactMappingViewId.equals(mapping))
390                         {
391                             return FacesServletMapping.createExactMapping(prefixedExactMappingViewId);
392                         }
393                     }
394                 }
395             }
396         }
397 
398         return null;
399     }
400     
401     
402     public static FacesServletMapping getGenericPrefixOrSuffixMapping(FacesContext facesContext)
403     {
404         if (!ExternalContextUtils.isPortlet(facesContext.getExternalContext()))
405         {
406             Object context = facesContext.getExternalContext().getContext();
407             if (context instanceof ServletContext)
408             {
409                 ServletRegistrationInfo facesServletRegistration =
410                         getFacesServletRegistration(facesContext, (ServletContext) context, true);
411                 if (facesServletRegistration != null)
412                 {
413                     for (String mapping : facesServletRegistration.getMappings())
414                     {
415                         if (isExtensionMapping(mapping))
416                         {
417                             String extension = extractExtension(mapping);
418                             return FacesServletMapping.createExtensionMapping(extension);
419                         }
420                         else if (isPrefixMapping(mapping))
421                         {
422                             String prefix = extractPrefix(mapping);
423                             return FacesServletMapping.createPrefixMapping(prefix);
424                         }
425                     }
426                 }
427             }
428         }
429         
430         return null;
431     }
432     
433     
434     
435     private static String extractExtensionFromUrl(String url)
436     {
437         int slashPos = url.lastIndexOf('/');
438         int extensionPos = url.lastIndexOf('.');
439         if (extensionPos > -1 && extensionPos > slashPos)
440         {
441             return url.substring(extensionPos);
442         }
443         
444         return null;
445     }
446     
447     private static boolean isExtensionMapping(String mapping)
448     {
449         return mapping.startsWith("*.");
450     }
451     
452     private static String extractExtension(String mapping)
453     {
454         return mapping.substring(1);
455     }
456     
457     private static boolean isPrefixMapping(String mapping)
458     {
459         return mapping.startsWith("/") && mapping.endsWith("/*");
460     }
461     
462     private static String extractPrefix(String mapping)
463     {
464         return mapping.substring(0, mapping.length() - 2);
465     }
466 }