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.util;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.lang.reflect.Method;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.context.ExternalContext;
28  import javax.servlet.ServletContext;
29  import javax.servlet.ServletRequest;
30  import javax.servlet.ServletResponseWrapper;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  /**
35   * This provides some functionality for determining some things about the
36   * native request object that is not provided by JSF.  This class is useful
37   * for use in places where Portlet API's may or may not be present and can
38   * also provide access to some request-specific items which are not available on
39   * the JSF ExternalContext.  If portlet API's are not present, this class simply 
40   * handles the Servlet Request type.
41   */
42  public final class ExternalContextUtils
43  {
44  
45      /**
46       * Returns <code>true</code> if a particular class relating to the supplied
47       * request type is available on the current classpath or <code>false</code> 
48       * if it is not.  This class assumes that all containers have a servlet type
49       * request available, but the portlet request types are all dependant on the 
50       * portlet container being used.
51       * 
52       * @param type the RequestType to test
53       * @return a boolean value of <code>true</code> if the container contains the
54       *         request type in the classpath
55       * @since 2.0
56       */
57      public static final boolean isRequestTypeAvailable(RequestType type)
58      {
59          switch (type)
60          {
61          case SERVLET:
62              return true;
63  
64          case ACTION:
65          case RENDER:
66              return _PORTLET_CONTEXT_CLASS != null;
67  
68          case RESOURCE:
69          case EVENT:
70              return _PORTLET_RENDER_REQUEST_CLASS != null;
71  
72          default:
73              return false;
74          }
75      }
76  
77      /**
78       * Returns <code>true</code> if a particular request type is supported by the
79       * container.  For a request type to be supported, the required objects must
80       * be on the classpath AND and, in the case of Portlet RequestTypes, an 
81       * appropriate bridge must be avaialble which supports those objects.  This
82       * means that if the supplied RequestType is RESOURCE, the 
83       * javax.portlet.ResourceRequest object must be available on the classpath AND
84       * a bridge which supports the Portlet 2.0 specification would also need to be
85       * available.
86       * 
87       * @param type the RequestType to test
88       * @return a boolean value of <code>true</code> if the container supports the
89       *         current request type
90       * @since 2.0
91       */
92      public static final boolean isRequestTypeSupported(RequestType type)
93      {
94          switch (type)
95          {
96          case SERVLET:
97              return true;
98  
99          case ACTION:
100         case RENDER:
101             return _PORTLET_10_SUPPORTED;
102 
103         case RESOURCE:
104         case EVENT:
105             return _PORTLET_20_SUPPORTED;
106 
107         default:
108             return false;
109         }
110     }
111     
112     /**
113      * Returns the requestType of this ExternalContext.
114      * 
115      * @param externalContext the current external context
116      * @return the appropriate RequestType for this external context
117      * @see RequestType
118      */
119     public static final RequestType getRequestType(ExternalContext externalContext)
120     {
121         //Stuff is laid out strangely in this class in order to optimize
122         //performance.  We want to do as few instanceof's as possible so
123         //things are laid out according to the expected frequency of the
124         //various requests occurring.
125         if(_PORTLET_10_SUPPORTED || _PORTLET_20_SUPPORTED)
126         {
127             if (_PORTLET_CONTEXT_CLASS.isInstance(externalContext.getContext()))
128             {
129                 //We are inside of a portlet container
130                 Object request = externalContext.getRequest();
131                 
132                 if(_PORTLET_RENDER_REQUEST_CLASS.isInstance(request))
133                 {
134                     return RequestType.RENDER;
135                 }
136                 
137                 if(_PORTLET_RESOURCE_REQUEST_CLASS != null)
138                 {
139                     if(_PORTLET_ACTION_REQUEST_CLASS.isInstance(request))
140                     {
141                         return RequestType.ACTION;
142                     }
143 
144                     //We are in a JSR-286 container
145                     if(_PORTLET_RESOURCE_REQUEST_CLASS.isInstance(request))
146                     {
147                         return RequestType.RESOURCE;
148                     }
149                     
150                     return RequestType.EVENT;
151                 }
152                 
153                 return RequestType.ACTION;
154             }
155         }
156         
157         return RequestType.SERVLET;
158     }
159 
160     /**
161      * This method is used when a ExternalContext object is not available,
162      * like in TomahawkFacesContextFactory.
163      * 
164      * According to TOMAHAWK-1331, the object context could receive an
165      * instance of javax.portlet.PortletContext or javax.portlet.PortletConfig,
166      * so we check both cases.
167      * 
168      * @param context
169      * @param request
170      * @return
171      */
172     public static final RequestType getRequestType(Object context, Object request)
173     {
174         //Stuff is laid out strangely in this class in order to optimize
175         //performance.  We want to do as few instanceof's as possible so
176         //things are laid out according to the expected frequency of the
177         //various requests occurring.
178 
179         if(_PORTLET_10_SUPPORTED || _PORTLET_20_SUPPORTED)
180         {
181             if (_PORTLET_CONFIG_CLASS.isInstance(context) ||
182                 _PORTLET_CONTEXT_CLASS.isInstance(context))
183             {
184                 //We are inside of a portlet container
185                 
186                 if(_PORTLET_RENDER_REQUEST_CLASS.isInstance(request))
187                 {
188                     return RequestType.RENDER;
189                 }
190                 
191                 if(_PORTLET_RESOURCE_REQUEST_CLASS != null)
192                 {
193                     if(_PORTLET_ACTION_REQUEST_CLASS.isInstance(request))
194                     {
195                         return RequestType.ACTION;
196                     }
197 
198                     //We are in a JSR-286 container
199                     if(_PORTLET_RESOURCE_REQUEST_CLASS.isInstance(request))
200                     {
201                         return RequestType.RESOURCE;
202                     }
203                     
204                     return RequestType.EVENT;
205                 }
206                 
207                 return RequestType.ACTION;
208             }
209         }
210         
211         return RequestType.SERVLET;
212     }
213 
214     /**
215      * Returns the current active session id or <code>null</code> if there is
216      * none.  If a session is not already created, this method will create one
217      * for you.
218      * 
219      * @param ec the current external context
220      * @return a string containing the requestedSessionId
221      */
222     public static String getSessionId(ExternalContext ec)
223     {
224         return getSessionId(ec, true);
225     }
226 
227     /**
228      * Returns the current active session id or <code>null</code> if there is
229      * none.
230      * 
231      * @param ec the current external context
232      * @param create create a new session if one is not created
233      * @return a string containing the requestedSessionId
234      */
235     public static String getSessionId(ExternalContext ec, boolean create)
236     {
237         Object session = ec.getSession(create);
238         return (null != session) ? (String) _runMethod(session, "getId") : null;
239     }
240 
241     /**
242      * Returns the session ID for the client, or <code>null</code> if there is none.
243      *
244      * @param ec the current external context
245      * @return a string containing the requestedSessionId
246      */
247     public static String getRequestedSessionId(ExternalContext ec)
248     {
249         return (String) _runMethod(ec.getRequest(), "getRequestedSessionId");
250     }
251 
252     /**
253      * Checks if the requested session ID is still valid.
254      *
255      * @param ec the current external context
256      * @return a boolean containing <code>true</code> if the request session is
257      *         valid or <code>false</code> if it is not
258      */
259     public static boolean isRequestedSessionIdValid(ExternalContext ec)
260     {
261         return (Boolean) _runMethod(ec.getRequest(),
262                 "isRequestedSessionIdValid");
263     }
264 
265     /**
266      * Returns the contextPath of the ServletContext or <code>null</code> for portlets
267      *
268      * @param ec the current external context
269      * @return a String containing the servletContextPath
270      */
271     public static String getServletContextPath(ExternalContext ec)
272     {
273         if (!isPortlet(ec))
274         {
275             return ((ServletContext) ec.getContext()).getContextPath();
276         }
277         else
278         {
279             return null;
280         }
281     }
282 
283     /**
284      * Returns the contextPath of the ServletRequest or <code>null</code> for portlet requests
285      *
286      * @param ec the current external context
287      * @return a String containing the request context path
288      * @see ExternalContext#getRequestContextPath()
289      * 
290      * @deprecated use ExternalContext.getRequestContextPath() as of JSF 1.2.  This method
291      *             does not appropriately handle portlet environments, but the functionality
292      *             is maintained to prevent needing to change the contract.
293      */
294     @Deprecated
295     public static String getRequestContextPath(ExternalContext ec)
296     {
297         if (!isPortlet(ec))
298         {
299             return ec.getRequestContextPath();
300         }
301         else
302         {
303             return null;
304         }
305     }
306 
307     /**
308      * Returns the requestURI of the HttpServletRequest or <code>null</code> for 
309      * portlet requests
310      *
311      * @param ec the current external context
312      * @return A string containing the current request uri
313      */
314     public static String getRequestURI(ExternalContext ec)
315     {
316         if (!isPortlet(ec))
317         {
318             return ((HttpServletRequest) ec.getRequest()).getRequestURI();
319         }
320         else
321         {
322             return null;
323         }
324     }
325 
326     /**
327      * Returns the character encoding or <code>null</code> if there isn't any
328      *
329      * @param ec the current external context
330      * @return a string containing the request's character encoding
331      * @see ExternalContext#getRequestCharacterEncoding()
332      * 
333      * @deprecated replaced by an API in JSF.  Use ExternalContext.getRequestCharacterEncoding()
334      */
335     @Deprecated
336     public static String getCharacterEncoding(ExternalContext ec)
337     {
338         return ec.getRequestCharacterEncoding();
339     }
340 
341     /**
342      * Returns the name of the underlying context or <code>null</code> if something
343      * went wrong in trying to retrieve the context.
344      * 
345      * @param ec the current external context
346      * @return a String containing the context name
347      */
348     public static String getContextName(ExternalContext ec)
349     {
350         try
351         {
352             if (isPortlet(ec))
353             {
354                 return (String) _runMethod(ec.getContext(),
355                         "getPortletContextName");
356             }
357             else
358             {
359                 return ((ServletContext) ec.getContext())
360                         .getServletContextName();
361             }
362         }
363         catch (final ClassCastException e)
364         {
365             _LOG.severe(e.getMessage());
366         }
367         return null;
368     }
369 
370     /**
371      * Returns the name and version of the underlying servlet container or <code>null</code> if something
372      * went wrong in trying to retrieve the context.
373      *
374      * @param ec the current external context
375      * @return a String containing the name and version of the underlying servlet container
376      */
377     public static String getServerInfo(ExternalContext ec)
378     {
379         try
380         {
381             if (isPortlet(ec))
382             {
383                 return (String) _runMethod(ec.getContext(), "getServerInfo");
384             }
385             else
386             {
387                 return ((ServletContext) ec.getContext()).getServerInfo();
388             }
389         }
390         catch (final ClassCastException e)
391         {
392             _LOG.severe(e.getMessage());
393         }
394         return null;
395     }
396 
397     /**
398      * Returns the content length or -1 if the unknown.
399      *
400      * @param ec the current external context
401      * @return the length or -1 if the length is unknown
402      */
403     public static int getContentLength(ExternalContext ec)
404     {
405         if (isRequestFromClient(ec))
406         {
407             return (Integer) _runMethod(ec.getRequest(), "getContentLength");
408         }
409 
410         return -1;
411     }
412 
413     /**
414      * Returns the content type from the current externalContext or
415      * <code>null</code> if unknown.
416      *
417      * @param ec the current external context
418      * @return a String contining the the content type or <code>null</code>
419      * @see ExternalContext#getRequestContentType()
420      *
421      * @deprecated use ExternalContext.getRequestContentType()
422      */
423     @Deprecated
424     public static String getContentType(ExternalContext ec)
425     {
426         return ec.getRequestContentType();
427     }
428 
429     /**
430      * Returns the request input stream if one is available
431      *
432      * @param ec the current external context
433      * @return the request's input stream
434      * @throws IOException if there was a problem getting the input stream
435      */
436     public static InputStream getRequestInputStream(ExternalContext ec)
437             throws IOException
438     {
439         RequestType type = getRequestType(ec);
440         if (type.isRequestFromClient())
441         {
442             Object req = ec.getRequest();
443             if (type.isPortlet())
444             {
445                 return (InputStream) _runMethod(req, "getPortletInputStream");
446             }
447             else
448             {
449                 return ((ServletRequest) ec.getRequest()).getInputStream();
450             }
451         }
452 
453         return null;
454     }
455 
456     /**
457      * Returns <code>true</code> if this externalContext represents an "action". 
458      * An action request is any ServletRequest or a portlet ActionRequest or 
459      * ResourceRequest.
460      *
461      * @param ec the current external context
462      * @return a boolean of <code>true</code> if this request is an action-type
463      *         request.
464      * @see #isRequestFromClient(ExternalContext)
465      *         
466      * @deprecated replaced with {@link #isRequestFromClient(ExternalContext)}
467      */
468     @Deprecated
469     public static boolean isAction(ExternalContext ec)
470     {
471         return isRequestFromClient(ec);
472     }
473 
474     /**
475      * Returns the value of {@link RequestType#isPortlet()} for the current
476      * RequestType. This is a convenience function designed to perform a quick
477      * check of the current request. If more capabilities need to be tested for
478      * the given request, then it is more efficient to pull this information from
479      * the RequestType itself.
480      * 
481      * @param ec the current external context
482      * @return a boolean value of <code>true</code> if the current RequestType
483      *         is a portlet request.
484      * 
485      * @see RequestType#isPortlet()
486      * @see #getRequestType(ExternalContext)
487      */
488     public static boolean isPortlet(ExternalContext ec)
489     {
490         return getRequestType(ec).isPortlet();
491     }
492 
493     /**
494      * Returns the value of {@link RequestType#isResponseWritable()} for the
495      * current RequestType. This is a convenience function designed to perform a
496      * quick check of the current request. If more capabilities need to be tested
497      * for the given request, then it is more efficient to pull this information
498      * from the RequestType itself.
499      * 
500      * @param ec the current external context
501      * @return a boolean value of <code>true</code> if the current RequestType
502      *         is a "render" type response.
503      * 
504      * @see RequestType#isResponseWritable()
505      * @see #getRequestType(ExternalContext)
506      * @since 2.0
507      */
508     public static final boolean isResponseWritable(ExternalContext ec)
509     {
510         return getRequestType(ec).isResponseWritable();
511     }
512 
513     /**
514      * Returns the value of {@link RequestType#isRequestFromClient()} for the
515      * current RequestType. This is a convenience function designed to perform a
516      * quick check of the current request. If more capabilities need to be tested
517      * for the given request, then it is more efficient to pull this information
518      * from the RequestType itself.
519      * 
520      * @param ec the current external context
521      * @return a boolean value of <code>true</code> if the current RequestType
522      *         represents a request from the client.
523      * 
524      * @see RequestType#isResponseWritable()
525      * @see #getRequestType(ExternalContext)
526      * @since 2.0
527      */
528     public static final boolean isRequestFromClient(ExternalContext ec)
529     {
530         return getRequestType(ec).isRequestFromClient();
531     }
532 
533     /**
534      * Returns wherther of not this external context represents a true HttpServletRequest or
535      * not.  Some portal containers implement the PortletRequest/Response objects as 
536      * HttpServletRequestWrappers, and those objects should not be treated as an
537      * HttpServlerRequest.  As such, this method first tests to see if the request is
538      * a portlet request and, if not, then tests to see if the request is an instanceof
539      * HttpServletRequest.
540      * 
541      * @param ec the current external context
542      * @return a boolean value of <code>true</code> if the current request is an
543      *         HttpServletRequest
544      * @since 1.1
545      */
546     public static boolean isHttpServletRequest(ExternalContext ec)
547     {
548         return (!isPortlet(ec) && (ec.getRequest() instanceof HttpServletRequest));
549     }
550 
551     /**
552      * Returns an HttpServletResponse if one exists on the externalContext or null
553      * if it does not.  Please note that some portal environments implement the
554      * PortletRequest and Response objects as HttpServletRequest/Response objects.
555      * This method handles these types of requests properly and will therefore
556      * return null in portal environments.
557      * 
558      * @param ec
559      * @return an HttpServletResponse if we have one or null if we do not
560      * @since 4.0
561      */
562     public static HttpServletResponse getHttpServletResponse(ExternalContext ec)
563     {
564         if (isHttpServletRequest(ec))
565         {
566             return (HttpServletResponse) ec.getResponse();
567         }
568 
569         return null;
570     }
571 
572     /**
573      * Runs a method on an object and returns the result
574      * 
575      * @param obj the object to run the method on
576      * @param methodName the name of the method
577      * @return the results of the method run
578      */
579     private static Object _runMethod(Object obj, String methodName)
580     {
581         try
582         {
583             Method sessionIdMethod = obj.getClass().getMethod(methodName);
584             return sessionIdMethod.invoke(obj);
585         }
586         catch (Exception e)
587         {
588             return null;
589         }
590 
591     }
592 
593     // prevent this from being instantiated
594     private ExternalContextUtils()
595     {
596     }
597 
598     private static final Logger _LOG = Logger
599             .getLogger(ExternalContextUtils.class.getName());
600 
601     // =-= Scott O'Bryan =-=
602     // Performance enhancement. These will be needed anyway, let's not get them every time.
603     private static final Class<?> _PORTLET_ACTION_REQUEST_CLASS;
604     private static final Class<?> _PORTLET_RENDER_REQUEST_CLASS;
605     private static final Class<?> _PORTLET_RESOURCE_REQUEST_CLASS;
606     private static final Class<?> _PORTLET_CONTEXT_CLASS;
607     private static final boolean _PORTLET_10_SUPPORTED;
608     private static final boolean _PORTLET_20_SUPPORTED;
609     private static final Class<?> _PORTLET_CONFIG_CLASS;
610 
611     static
612     {
613         Class<?> context;
614         Class<?> config;
615         Class<?> actionRequest;
616         Class<?> renderRequest;
617         Class<?> resourceRequest;
618         boolean portlet20Supported = false;
619         boolean portlet10Supported = false;
620 
621         try
622         {
623             context = ClassLoaderUtils
624                     .loadClass("javax.portlet.PortletContext");
625             config = ClassLoaderUtils.loadClass("javax.portlet.PortletConfig");
626             actionRequest = ClassLoaderUtils
627                     .loadClass("javax.portlet.ActionRequest");
628             renderRequest = ClassLoaderUtils
629                     .loadClass("javax.portlet.RenderRequest");
630 
631             try
632             {
633                 resourceRequest = ClassLoaderUtils
634                         .loadClass("javax.portlet.ResourceRequest");
635             }
636             catch (ClassNotFoundException e)
637             {
638                 _LOG.fine("Portlet 2.0 API is not available on classpath.  Portlet 2.0 functionality is disabled");
639                 resourceRequest = null;
640             }
641         }
642         catch (final ClassNotFoundException e)
643         {
644             _LOG.fine("Portlet API is not available on the classpath.  Portlet configurations are disabled.");
645             context = null;
646             config = null;
647             actionRequest = null;
648             renderRequest = null;
649             resourceRequest = null;
650         }
651 
652         //Find bridge to tell if portal is supported
653         if (context != null)
654         {
655             // Portlet 1.0 API found. In this case we have to consider that exists alternate
656             // bridge implementations like in WebSphere and others.
657             portlet10Supported = true;
658 
659             try
660             {
661                 Class<?> bridge = ClassLoaderUtils
662                         .loadClass("javax.portlet.faces.Bridge");
663 
664                 if (bridge != null)
665                 {
666                     //Standard bridge defines a spec name which can be used to 
667                     //determine Portlet 2.0 Support.
668                     String specName = bridge.getPackage()
669                             .getSpecificationTitle();
670                     _LOG.fine("Found Bridge: " + specName);
671                     if (specName != null && specName.startsWith("Portlet 2"))
672                     {
673                         portlet20Supported = true;
674                     }
675 
676                     if (_LOG.isLoggable(Level.INFO))
677                     {
678                         String ver = (portlet20Supported) ? "2.0" : "1.0";
679                         _LOG.info("Portlet Environment Detected: " + ver);
680                     }
681                 }
682             }
683             catch (ClassNotFoundException e)
684             {
685                 _LOG.fine("Portlet API is present but Standard Apache Portlet Bridge is not. "
686                         + " This could happen if you are using an alternate Portlet Bridge solution.");
687 
688                 if (resourceRequest != null)
689                 {
690                     portlet20Supported = true;
691                 }
692             }
693         }
694 
695         _PORTLET_CONTEXT_CLASS = context;
696         _PORTLET_CONFIG_CLASS = config;
697         _PORTLET_ACTION_REQUEST_CLASS = actionRequest;
698         _PORTLET_RENDER_REQUEST_CLASS = renderRequest;
699         _PORTLET_RESOURCE_REQUEST_CLASS = resourceRequest;
700         _PORTLET_10_SUPPORTED = portlet10Supported;
701         _PORTLET_20_SUPPORTED = portlet20Supported;
702     }
703 
704     /**
705      * Trys to obtain a HttpServletResponse from the Response.
706      * Note that this method also trys to unwrap any ServletResponseWrapper
707      * in order to retrieve a valid HttpServletResponse.
708      * @param response
709      * @return if found, the HttpServletResponse, null otherwise
710      */
711     public static HttpServletResponse getHttpServletResponse(Object response)
712     {
713         // unwrap the response until we find a HttpServletResponse
714         while (response != null)
715         {
716             if (response instanceof HttpServletResponse)
717             {
718                 // found
719                 return (HttpServletResponse) response;
720             }
721             if (response instanceof ServletResponseWrapper)
722             {
723                 // unwrap
724                 response = ((ServletResponseWrapper) response).getResponse();
725             }
726             // no more possibilities to find a HttpServletResponse
727             break;
728         }
729         return null; // not found
730     }
731 
732     /**
733      * Trys to obtain a ResponseSwitch from the Response.
734      * @param response
735      * @return if found, the ResponseSwitch, null otherwise
736      */
737     /*
738     public static ResponseSwitch getResponseSwitch(Object response)
739     {
740         // unwrap the response until we find a ResponseSwitch
741         while (response != null)
742         {
743             if (response instanceof ResponseSwitch)
744             {
745                 // found
746                 return (ResponseSwitch) response;
747             }
748             if (response instanceof ServletResponseWrapper)
749             {
750                 // unwrap
751                 response = ((ServletResponseWrapper) response).getResponse();
752             }
753             // no more possibilities to find a ResponseSwitch
754             break; 
755         }
756         return null; // not found
757     }*/
758 
759     /**
760      * Try to create a ResponseSwitch for this response.
761      * @param response
762      * @return the created ResponseSwitch, if there is a ResponseSwitch 
763      *         implementation for the given response, null otherwise
764      */
765     /*
766     public static ResponseSwitch createResponseSwitch(Object response)
767     {
768         if (response instanceof HttpServletResponse)
769         {
770             return new HttpServletResponseSwitch((HttpServletResponse) response);
771         }
772         else if (response instanceof ServletResponse)
773         {
774             return new ServletResponseSwitch((ServletResponse) response);
775         }
776         return null;
777     }*/
778 
779 }