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