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 }