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 }