001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.flex; 029 030import org.opencms.ade.detailpage.CmsDetailPageResourceHandler; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.main.CmsLog; 034import org.opencms.util.CmsRequestUtil; 035 036import java.io.IOException; 037import java.util.HashSet; 038import java.util.List; 039import java.util.Map; 040import java.util.Set; 041import java.util.Vector; 042 043import javax.servlet.ServletRequest; 044import javax.servlet.http.HttpServletRequest; 045import javax.servlet.http.HttpServletResponse; 046 047import org.apache.commons.logging.Log; 048 049/** 050 * Controller for getting access to the CmsObject, should be used as a 051 * request attribute.<p> 052 * 053 * @since 6.0.0 054 */ 055public class CmsFlexController { 056 057 /** 058 * Information about where to redirect to. 059 */ 060 public static class RedirectInfo { 061 062 /** True if a permanent redirect should be used. */ 063 private boolean m_permanent; 064 065 /** The redirect target. */ 066 private String m_target; 067 068 /** 069 * Creates a new instance. 070 * 071 * @param target the redirect target 072 * @param permanent true if a permanent redirect should be used 073 */ 074 public RedirectInfo(String target, boolean permanent) { 075 076 m_target = target; 077 m_permanent = permanent; 078 079 } 080 081 /** 082 * Executes the redirect on the given response with the stored information. 083 * 084 * @param res the res to use for executing the redirect 085 */ 086 public void executeRedirect(HttpServletResponse res) throws IOException { 087 088 if (isPermanent()) { 089 res.setHeader(CmsRequestUtil.HEADER_LOCATION, getTarget()); 090 res.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 091 } else { 092 res.sendRedirect(getTarget()); 093 } 094 095 } 096 097 /** 098 * Gets the redirect target. 099 * 100 * @return the redirect target 101 */ 102 public String getTarget() { 103 104 return m_target; 105 } 106 107 /** 108 * Returns true if a permanent redirect should be used. 109 * 110 * @return true if a permanent redirect should be used 111 */ 112 public boolean isPermanent() { 113 114 return m_permanent; 115 } 116 117 } 118 119 /** Constant for the controller request attribute name. */ 120 public static final String ATTRIBUTE_NAME = "org.opencms.flex.CmsFlexController"; 121 122 /** The log object for this class. */ 123 private static final Log LOG = CmsLog.getLog(CmsFlexController.class); 124 125 /** Set of uncacheable attributes. */ 126 private static Set<String> uncacheableAttributes = new HashSet<String>(); 127 128 /** Fake HTTP header used to store information about the content type in a CmsFlexResponse. Should never be actually sent to the client. */ 129 public static final String HEADER_OPENCMS_CONTENT_TYPE = "X-OpenCms-Content-Type"; 130 131 /** The CmsFlexCache where the result will be cached in, required for the dispatcher. */ 132 private CmsFlexCache m_cache; 133 134 /** The wrapped CmsObject provides JSP with access to the core system. */ 135 private CmsObject m_cmsObject; 136 137 /** List of wrapped RequestContext info object. */ 138 private List<CmsFlexRequestContextInfo> m_flexContextInfoList; 139 140 /** List of wrapped CmsFlexRequests. */ 141 private List<CmsFlexRequest> m_flexRequestList; 142 143 /** List of wrapped CmsFlexResponses. */ 144 private List<CmsFlexResponse> m_flexResponseList; 145 146 /** Indicates if this controller is currently in "forward" mode. */ 147 private boolean m_forwardMode; 148 149 /** Information about where to redirect to. */ 150 private RedirectInfo m_redirectInfo; 151 152 /** Wrapped top request. */ 153 private HttpServletRequest m_req; 154 155 /** Wrapped top response. */ 156 private HttpServletResponse m_res; 157 158 /** The CmsResource that was initialized by the original request, required for URI actions. */ 159 private CmsResource m_resource; 160 161 /** Indicates if the response should be streamed. */ 162 private boolean m_streaming; 163 164 /** Exception that was caught during inclusion of sub elements. */ 165 private Throwable m_throwable; 166 167 /** URI of a VFS resource that caused the exception. */ 168 private String m_throwableResourceUri; 169 170 /** Indicates if the request is the top request. */ 171 private boolean m_top; 172 173 /** 174 * Creates a new controller form the old one, exchanging just the provided OpenCms user context.<p> 175 * 176 * @param cms the OpenCms user context for this controller 177 * @param base the base controller 178 */ 179 public CmsFlexController(CmsObject cms, CmsFlexController base) { 180 181 m_cmsObject = cms; 182 m_resource = base.m_resource; 183 m_cache = base.m_cache; 184 m_req = base.m_req; 185 m_res = base.m_res; 186 m_streaming = base.m_streaming; 187 m_top = base.m_top; 188 m_flexRequestList = base.m_flexRequestList; 189 m_flexResponseList = base.m_flexResponseList; 190 m_flexContextInfoList = base.m_flexContextInfoList; 191 m_forwardMode = base.m_forwardMode; 192 m_throwableResourceUri = base.m_throwableResourceUri; 193 m_redirectInfo = base.m_redirectInfo; 194 } 195 196 /** 197 * Default constructor.<p> 198 * 199 * @param cms the initial CmsObject to wrap in the controller 200 * @param resource the file requested 201 * @param cache the instance of the flex cache 202 * @param req the current request 203 * @param res the current response 204 * @param streaming indicates if the response is streaming 205 * @param top indicates if the response is the top response 206 */ 207 public CmsFlexController( 208 CmsObject cms, 209 CmsResource resource, 210 CmsFlexCache cache, 211 HttpServletRequest req, 212 HttpServletResponse res, 213 boolean streaming, 214 boolean top) { 215 216 m_cmsObject = cms; 217 m_resource = resource; 218 m_cache = cache; 219 m_req = req; 220 m_res = res; 221 m_streaming = streaming; 222 m_top = top; 223 m_flexRequestList = new Vector<CmsFlexRequest>(); 224 m_flexResponseList = new Vector<CmsFlexResponse>(); 225 m_flexContextInfoList = new Vector<CmsFlexRequestContextInfo>(); 226 m_forwardMode = false; 227 m_throwableResourceUri = null; 228 } 229 230 /** 231 * Returns the wrapped CmsObject form the provided request, or <code>null</code> if the 232 * request is not running inside OpenCms.<p> 233 * 234 * @param req the current request 235 * @return the wrapped CmsObject 236 */ 237 public static CmsObject getCmsObject(ServletRequest req) { 238 239 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 240 if (controller != null) { 241 return controller.getCmsObject(); 242 } else { 243 return null; 244 } 245 } 246 247 /** 248 * Returns the controller from the given request, or <code>null</code> if the 249 * request is not running inside OpenCms.<p> 250 * 251 * @param req the request to get the controller from 252 * 253 * @return the controller from the given request, or <code>null</code> if the request is not running inside OpenCms 254 */ 255 public static CmsFlexController getController(ServletRequest req) { 256 257 return (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 258 } 259 260 /** 261 * Provides access to a root cause Exception that might have occurred in a complex include scenario.<p> 262 * 263 * @param req the current request 264 * 265 * @return the root cause exception or null if no root cause exception is available 266 * 267 * @see #getThrowable() 268 */ 269 public static Throwable getThrowable(ServletRequest req) { 270 271 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 272 if (controller != null) { 273 return controller.getThrowable(); 274 } else { 275 return null; 276 } 277 } 278 279 /** 280 * Provides access to URI of a VFS resource that caused an exception that might have occurred in a complex include scenario.<p> 281 * 282 * @param req the current request 283 * 284 * @return to URI of a VFS resource that caused an exception, or <code>null</code> 285 * 286 * @see #getThrowableResourceUri() 287 */ 288 public static String getThrowableResourceUri(ServletRequest req) { 289 290 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 291 if (controller != null) { 292 return controller.getThrowableResourceUri(); 293 } else { 294 return null; 295 } 296 } 297 298 /** 299 * Checks if the provided request is running in OpenCms and the current users project is the online project.<p> 300 * 301 * @param req the current request 302 * 303 * @return <code>true</code> if the request is running in OpenCms and the current users project is 304 * the online project, <code>false</code> otherwise 305 */ 306 public static boolean isCmsOnlineRequest(ServletRequest req) { 307 308 if (req == null) { 309 return false; 310 } 311 return getController(req).getCmsObject().getRequestContext().getCurrentProject().isOnlineProject(); 312 } 313 314 /** 315 * Checks if the provided request is running in OpenCms.<p> 316 * 317 * @param req the current request 318 * 319 * @return <code>true</code> if the request is running in OpenCms, <code>false</code> otherwise 320 */ 321 public static boolean isCmsRequest(ServletRequest req) { 322 323 return ((req != null) && (req.getAttribute(ATTRIBUTE_NAME) != null)); 324 } 325 326 /** 327 * Checks if the request has the "If-Modified-Since" header set, and if so, 328 * if the header date value is equal to the provided last modification date.<p> 329 * 330 * @param req the request to set the "If-Modified-Since" date header from 331 * @param dateLastModified the date to compare the header with 332 * 333 * @return <code>true</code> if the header is set and the header date is equal to the provided date 334 */ 335 public static boolean isNotModifiedSince(HttpServletRequest req, long dateLastModified) { 336 337 // check if the request contains a last modified header 338 try { 339 long lastModifiedHeader = req.getDateHeader(CmsRequestUtil.HEADER_IF_MODIFIED_SINCE); 340 // if last modified header is set (> -1), compare it to the requested resource 341 return ((lastModifiedHeader > -1) && (((dateLastModified / 1000) * 1000) == lastModifiedHeader)); 342 } catch (Exception ex) { 343 // some clients (e.g. User-Agent: BlackBerry7290/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/111) 344 // send an invalid "If-Modified-Since" header (e.g. in german locale) 345 // which breaks with http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html 346 // this has to be caught because the subsequent request for the 500 error handler 347 // would run into the same exception. 348 LOG.warn( 349 Messages.get().getBundle().key( 350 Messages.ERR_HEADER_IFMODIFIEDSINCE_FORMAT_3, 351 new Object[] { 352 CmsRequestUtil.HEADER_IF_MODIFIED_SINCE, 353 req.getHeader(CmsRequestUtil.HEADER_USER_AGENT), 354 req.getHeader(CmsRequestUtil.HEADER_IF_MODIFIED_SINCE)})); 355 } 356 return false; 357 } 358 359 /** 360 * Tells the flex controller to never cache the given attribute.<p> 361 * 362 * @param attributeName the attribute which shouldn't be cached 363 */ 364 public static void registerUncacheableAttribute(String attributeName) { 365 366 uncacheableAttributes.add(attributeName); 367 } 368 369 /** 370 * Removes the controller attribute from a request.<p> 371 * 372 * @param req the request to remove the controller from 373 */ 374 public static void removeController(ServletRequest req) { 375 376 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 377 if (controller != null) { 378 controller.clear(); 379 } 380 } 381 382 /** 383 * Stores the given controller in the given request (using a request attribute).<p> 384 * 385 * @param req the request where to store the controller in 386 * @param controller the controller to store 387 */ 388 public static void setController(ServletRequest req, CmsFlexController controller) { 389 390 req.setAttribute(CmsFlexController.ATTRIBUTE_NAME, controller); 391 } 392 393 /** 394 * Sets the <code>Expires</code> date header for a given http request.<p> 395 * 396 * Also sets the <code>cache-control: max-age</code> header to the time of the expiration. 397 * A certain upper limit is imposed on the expiration date parameter to ensure the resources are 398 * not cached to long in proxies. This can be controlled by the <code>maxAge</code> parameter. 399 * If <code>maxAge</code> is lower then 0, then a default max age of 86400000 msec (1 day) is used.<p> 400 * 401 * @param res the response to set the "Expires" date header for 402 * @param maxAge maximum amount of time in milliseconds the response remains valid 403 * @param dateExpires the date to set (if this is not in the future, it is ignored) 404 */ 405 public static void setDateExpiresHeader(HttpServletResponse res, long dateExpires, long maxAge) { 406 407 long now = System.currentTimeMillis(); 408 if ((dateExpires > now) && (dateExpires != CmsResource.DATE_EXPIRED_DEFAULT)) { 409 410 // important: many caches (browsers or proxy) use the "Expires" header 411 // to avoid re-loading of pages that are not expired 412 // while this is right in general, no changes before the expiration date 413 // will be displayed 414 // therefore it is better to not use an expiration to far in the future 415 416 // if no valid max age is set, restrict it to 24 hrs 417 if (maxAge < 0L) { 418 maxAge = 86400000; 419 } 420 421 if ((dateExpires - now) > maxAge) { 422 // set "Expires" header max one day into the future 423 dateExpires = now + maxAge; 424 } 425 res.setDateHeader(CmsRequestUtil.HEADER_EXPIRES, dateExpires); 426 427 // setting the "Expires" header only is not sufficient - even expired documents seems to be cached 428 // therefore, the "cache-control: max-age" is also set 429 res.setHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_MAX_AGE + (maxAge / 1000L)); 430 } 431 } 432 433 /** 434 * Sets the "last modified" date header for a given http request.<p> 435 * 436 * @param res the response to set the "last modified" date header for 437 * @param dateLastModified the date to set (if this is lower then 0, the current time is set) 438 */ 439 public static void setDateLastModifiedHeader(HttpServletResponse res, long dateLastModified) { 440 441 if (dateLastModified > -1) { 442 // set date last modified header (precision is only second, not millisecond 443 res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, (dateLastModified / 1000) * 1000); 444 } else { 445 // this resource can not be optimized for "last modified", use current time as header 446 res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, System.currentTimeMillis()); 447 // avoiding issues with IE8+ 448 res.addHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, "public, max-age=0"); 449 } 450 } 451 452 /** 453 * Method overload from the standard HttpServletResponse API.<p> 454 * 455 * The method is added to the controller itself, 456 * to automatically have the correct choice for the response to set the header. 457 * 458 * @param name 459 * @param date 460 * 461 * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long) 462 */ 463 public void addDateHeader(String name, long date) { 464 465 getCurrentResponse().addDateHeader(name, date); 466 } 467 468 /** 469 * Method overload from the standard HttpServletResponse API.<p> 470 * 471 * The method is added to the controller itself, 472 * to automatically have the correct choice for the response to set the header. 473 * 474 * @param name 475 * @param value 476 * 477 * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String) 478 */ 479 public void addHeader(String name, String value) { 480 481 getCurrentResponse().addHeader(name, value); 482 } 483 484 /** 485 * Method overload from the standard HttpServletResponse API.<p> 486 * 487 * The method is added to the controller itself, 488 * to automatically have the correct choice for the response to set the header. 489 * 490 * @param name 491 * @param value 492 * 493 * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int) 494 */ 495 public void addIntHeader(String name, int value) { 496 497 getCurrentResponse().addIntHeader(name, value); 498 } 499 500 /** 501 * Clears all data of this controller.<p> 502 */ 503 public void clear() { 504 505 if (m_flexRequestList != null) { 506 m_flexRequestList.clear(); 507 } 508 m_flexRequestList = null; 509 if (m_flexResponseList != null) { 510 m_flexResponseList.clear(); 511 } 512 m_flexResponseList = null; 513 if (m_req != null) { 514 m_req.removeAttribute(ATTRIBUTE_NAME); 515 } 516 m_req = null; 517 m_res = null; 518 m_cmsObject = null; 519 m_resource = null; 520 m_cache = null; 521 m_throwable = null; 522 } 523 524 /** 525 * Returns the CmsFlexCache instance where all results from this request will be cached in.<p> 526 * 527 * This is public so that pages like the Flex Cache Administration page 528 * have a way to access the cache object.<p> 529 * 530 * @return the CmsFlexCache instance where all results from this request will be cached in 531 */ 532 public CmsFlexCache getCmsCache() { 533 534 return m_cache; 535 } 536 537 /** 538 * Returns the wrapped CmsObject.<p> 539 * 540 * @return the wrapped CmsObject 541 */ 542 public CmsObject getCmsObject() { 543 544 return m_cmsObject; 545 } 546 547 /** 548 * This method provides access to the top-level CmsResource of the request 549 * which is of a type that supports the FlexCache, 550 * i.e. usually the CmsFile that is identical to the file uri requested by the user, 551 * not he current included element.<p> 552 * 553 * @return the requested top-level CmsFile 554 */ 555 public CmsResource getCmsResource() { 556 557 return m_resource; 558 } 559 560 /** 561 * Returns the current flex request.<p> 562 * 563 * @return the current flex request 564 */ 565 public CmsFlexRequest getCurrentRequest() { 566 567 return m_flexRequestList.get(m_flexRequestList.size() - 1); 568 } 569 570 /** 571 * Returns the current flex response.<p> 572 * 573 * @return the current flex response 574 */ 575 public CmsFlexResponse getCurrentResponse() { 576 577 return m_flexResponseList.get(m_flexResponseList.size() - 1); 578 } 579 580 /** 581 * Returns the combined "expires" date for all resources read during this request.<p> 582 * 583 * @return the combined "expires" date for all resources read during this request 584 */ 585 public long getDateExpires() { 586 587 int pos = m_flexContextInfoList.size() - 1; 588 if (pos < 0) { 589 // ensure a valid position is used 590 return CmsResource.DATE_EXPIRED_DEFAULT; 591 } 592 return (m_flexContextInfoList.get(pos)).getDateExpires(); 593 } 594 595 /** 596 * Returns the combined "last modified" date for all resources read during this request.<p> 597 * 598 * @return the combined "last modified" date for all resources read during this request 599 */ 600 public long getDateLastModified() { 601 602 int pos = m_flexContextInfoList.size() - 1; 603 if (pos < 0) { 604 // ensure a valid position is used 605 return CmsResource.DATE_RELEASED_DEFAULT; 606 } 607 return (m_flexContextInfoList.get(pos)).getDateLastModified(); 608 } 609 610 /** 611 * Gets the information about where to redirect to. 612 * 613 * @return the redirect information 614 */ 615 public RedirectInfo getRedirectInfo() { 616 617 return m_redirectInfo; 618 } 619 620 /** 621 * Returns the size of the response stack.<p> 622 * 623 * @return the size of the response stack 624 */ 625 public int getResponseStackSize() { 626 627 return m_flexResponseList.size(); 628 } 629 630 /** 631 * Returns an exception (Throwable) that was caught during inclusion of sub elements, 632 * or null if no exceptions where thrown in sub elements.<p> 633 * 634 * @return an exception (Throwable) that was caught during inclusion of sub elements 635 */ 636 public Throwable getThrowable() { 637 638 return m_throwable; 639 } 640 641 /** 642 * Returns the URI of a VFS resource that caused the exception that was caught during inclusion of sub elements, 643 * might return null if no URI information was available for the exception.<p> 644 * 645 * @return the URI of a VFS resource that caused the exception that was caught during inclusion of sub elements 646 */ 647 public String getThrowableResourceUri() { 648 649 return m_throwableResourceUri; 650 } 651 652 /** 653 * Returns the current http request.<p> 654 * 655 * @return the current http request 656 */ 657 public HttpServletRequest getTopRequest() { 658 659 return m_req; 660 } 661 662 /** 663 * Returns the current http response.<p> 664 * 665 * @return the current http response 666 */ 667 public HttpServletResponse getTopResponse() { 668 669 return m_res; 670 } 671 672 /** 673 * Returns <code>true</code> if the controller does not yet contain any requests.<p> 674 * 675 * @return <code>true</code> if the controller does not yet contain any requests 676 */ 677 public boolean isEmptyRequestList() { 678 679 return (m_flexRequestList != null) && m_flexRequestList.isEmpty(); 680 } 681 682 /** 683 * Returns <code>true</code> if this controller is currently in "forward" mode.<p> 684 * 685 * @return <code>true</code> if this controller is currently in "forward" mode 686 */ 687 public boolean isForwardMode() { 688 689 return m_forwardMode; 690 } 691 692 /** 693 * Returns <code>true</code> if the generated output of the response should 694 * be written to the stream directly.<p> 695 * 696 * @return <code>true</code> if the generated output of the response should be written to the stream directly 697 */ 698 public boolean isStreaming() { 699 700 return m_streaming; 701 } 702 703 /** 704 * Returns <code>true</code> if this controller was generated as top level controller.<p> 705 * 706 * If a resource (e.g. a JSP) is processed and it's content is included in 707 * another resource, then this will be <code>false</code>. 708 * 709 * @return <code>true</code> if this controller was generated as top level controller 710 * 711 * @see org.opencms.loader.I_CmsResourceLoader#dump(CmsObject, CmsResource, String, java.util.Locale, HttpServletRequest, HttpServletResponse) 712 * @see org.opencms.jsp.CmsJspActionElement#getContent(String) 713 */ 714 public boolean isTop() { 715 716 return m_top; 717 } 718 719 /** 720 * Removes the topmost request/response pair from the stack.<p> 721 */ 722 public void pop() { 723 724 if ((m_flexRequestList != null) && !m_flexRequestList.isEmpty()) { 725 m_flexRequestList.remove(m_flexRequestList.size() - 1); 726 } 727 if ((m_flexResponseList != null) && !m_flexRequestList.isEmpty()) { 728 m_flexResponseList.remove(m_flexResponseList.size() - 1); 729 } 730 if ((m_flexContextInfoList != null) && !m_flexContextInfoList.isEmpty()) { 731 CmsFlexRequestContextInfo info = m_flexContextInfoList.remove(m_flexContextInfoList.size() - 1); 732 if (m_flexContextInfoList.size() > 0) { 733 (m_flexContextInfoList.get(0)).merge(info); 734 updateRequestContextInfo(); 735 } 736 } 737 } 738 739 /** 740 * Adds another flex request/response pair to the stack.<p> 741 * 742 * @param req the request to add 743 * @param res the response to add 744 */ 745 public void push(CmsFlexRequest req, CmsFlexResponse res) { 746 747 m_flexRequestList.add(req); 748 m_flexResponseList.add(res); 749 m_flexContextInfoList.add(new CmsFlexRequestContextInfo()); 750 updateRequestContextInfo(); 751 } 752 753 /** 754 * Removes request attributes which shouldn't be cached in flex cache entries from a map.<p> 755 * 756 * @param attributeMap the map of attributes 757 */ 758 public void removeUncacheableAttributes(Map<String, Object> attributeMap) { 759 760 for (String uncacheableAttribute : uncacheableAttributes) { 761 attributeMap.remove(uncacheableAttribute); 762 } 763 attributeMap.remove(CmsFlexController.ATTRIBUTE_NAME); 764 attributeMap.remove(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE); 765 attributeMap.remove(CmsDetailPageResourceHandler.ATTR_DETAIL_FUNCTION_PAGE); 766 } 767 768 /** 769 * Special method for setting the content type from a JSP in a way that is compatible with both Tomcat and Jetty and also works with the Flex Cache. 770 * 771 * <p>This is implemented as setting a fake HTTP header which is then translated back to a call to setContentType() in CmsJspLoader.dispatchJsp(). 772 * 773 * @param contentType the content type to set 774 */ 775 public void setContentType(String contentType) { 776 777 getCurrentResponse().setHeader(HEADER_OPENCMS_CONTENT_TYPE, contentType); 778 } 779 780 /** 781 * Method overload from the standard HttpServletResponse API.<p> 782 * 783 * The method is added to the controller itself, 784 * to automatically have the correct choice for the response to set the header. 785 * 786 * @param name 787 * @param date 788 * 789 * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long) 790 */ 791 public void setDateHeader(String name, long date) { 792 793 getCurrentResponse().setDateHeader(name, date); 794 } 795 796 /** 797 * Sets the value of the "forward mode" flag.<p> 798 * 799 * @param value the forward mode to set 800 */ 801 public void setForwardMode(boolean value) { 802 803 m_forwardMode = value; 804 } 805 806 /** 807 * Method overload from the standard HttpServletResponse API.<p> 808 * 809 * The method is added to the controller itself, 810 * to automatically have the correct choice for the response to set the header. 811 * 812 * @param name 813 * @param value 814 * 815 * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String) 816 */ 817 public void setHeader(String name, String value) { 818 819 getCurrentResponse().setHeader(name, value); 820 } 821 822 /** 823 * Method overload from the standard HttpServletResponse API.<p> 824 * 825 * The method is added to the controller itself, 826 * to automatically have the correct choice for the response to set the header. 827 * 828 * @param name 829 * @param value 830 * 831 * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int) 832 */ 833 public void setIntHeader(String name, int value) { 834 835 getCurrentResponse().setIntHeader(name, value); 836 } 837 838 /** 839 * Sets the information about where to redirect to. 840 * 841 * @param redirectInfo the redirect information 842 */ 843 public void setRedirectInfo(RedirectInfo redirectInfo) { 844 845 m_redirectInfo = redirectInfo; 846 } 847 848 /** 849 * Sets an exception (Throwable) that was caught during inclusion of sub elements.<p> 850 * 851 * If another exception is already set in this controller, then the additional exception 852 * is ignored.<p> 853 * 854 * @param throwable the exception (Throwable) to set 855 * @param resource the URI of the VFS resource the error occurred on (might be <code>null</code> if unknown) 856 * 857 * @return the exception stored in the controller 858 */ 859 public Throwable setThrowable(Throwable throwable, String resource) { 860 861 if (m_throwable == null) { 862 m_throwable = throwable; 863 m_throwableResourceUri = resource; 864 } else { 865 if (LOG.isDebugEnabled()) { 866 if (resource != null) { 867 LOG.debug( 868 Messages.get().getBundle().key(Messages.LOG_FLEXCONTROLLER_IGNORED_EXCEPTION_1, resource)); 869 } else { 870 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCONTROLLER_IGNORED_EXCEPTION_0)); 871 } 872 } 873 } 874 return m_throwable; 875 } 876 877 /** 878 * Puts the response in a suspended state.<p> 879 */ 880 public void suspendFlexResponse() { 881 882 for (int i = 0; i < m_flexResponseList.size(); i++) { 883 CmsFlexResponse res = m_flexResponseList.get(i); 884 res.setSuspended(true); 885 } 886 } 887 888 /** 889 * Updates the "last modified" date and the "expires" date 890 * for all resources read during this request with the given values.<p> 891 * 892 * The currently stored value for "last modified" is only updated with the new value if 893 * the new value is either larger (i.e. newer) then the stored value, 894 * or if the new value is less then zero, which indicates that the "last modified" 895 * optimization can not be used because the element is dynamic.<p> 896 * 897 * The stored "expires" value is only updated if the new value is smaller 898 * then the stored value.<p> 899 * 900 * @param dateLastModified the value to update the "last modified" date with 901 * @param dateExpires the value to update the "expires" date with 902 */ 903 public void updateDates(long dateLastModified, long dateExpires) { 904 905 int pos = m_flexContextInfoList.size() - 1; 906 if (pos < 0) { 907 // ensure a valid position is used 908 return; 909 } 910 (m_flexContextInfoList.get(pos)).updateDates(dateLastModified, dateExpires); 911 } 912 913 /** 914 * Updates the context info of the request context.<p> 915 */ 916 private void updateRequestContextInfo() { 917 918 if ((m_flexContextInfoList != null) && !m_flexContextInfoList.isEmpty()) { 919 m_cmsObject.getRequestContext().setAttribute( 920 CmsRequestUtil.HEADER_LAST_MODIFIED, 921 m_flexContextInfoList.get(m_flexContextInfoList.size() - 1)); 922 } 923 } 924}