001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://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: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://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 /** The CmsFlexCache where the result will be cached in, required for the dispatcher. */ 129 private CmsFlexCache m_cache; 130 131 /** The wrapped CmsObject provides JSP with access to the core system. */ 132 private CmsObject m_cmsObject; 133 134 /** List of wrapped RequestContext info object. */ 135 private List<CmsFlexRequestContextInfo> m_flexContextInfoList; 136 137 /** List of wrapped CmsFlexRequests. */ 138 private List<CmsFlexRequest> m_flexRequestList; 139 140 /** List of wrapped CmsFlexResponses. */ 141 private List<CmsFlexResponse> m_flexResponseList; 142 143 /** Indicates if this controller is currently in "forward" mode. */ 144 private boolean m_forwardMode; 145 146 /** Information about where to redirect to. */ 147 private RedirectInfo m_redirectInfo; 148 149 /** Wrapped top request. */ 150 private HttpServletRequest m_req; 151 152 /** Wrapped top response. */ 153 private HttpServletResponse m_res; 154 155 /** The CmsResource that was initialized by the original request, required for URI actions. */ 156 private CmsResource m_resource; 157 158 /** Indicates if the response should be streamed. */ 159 private boolean m_streaming; 160 161 /** Exception that was caught during inclusion of sub elements. */ 162 private Throwable m_throwable; 163 164 /** URI of a VFS resource that caused the exception. */ 165 private String m_throwableResourceUri; 166 167 /** Indicates if the request is the top request. */ 168 private boolean m_top; 169 170 /** 171 * Creates a new controller form the old one, exchanging just the provided OpenCms user context.<p> 172 * 173 * @param cms the OpenCms user context for this controller 174 * @param base the base controller 175 */ 176 public CmsFlexController(CmsObject cms, CmsFlexController base) { 177 178 m_cmsObject = cms; 179 m_resource = base.m_resource; 180 m_cache = base.m_cache; 181 m_req = base.m_req; 182 m_res = base.m_res; 183 m_streaming = base.m_streaming; 184 m_top = base.m_top; 185 m_flexRequestList = base.m_flexRequestList; 186 m_flexResponseList = base.m_flexResponseList; 187 m_flexContextInfoList = base.m_flexContextInfoList; 188 m_forwardMode = base.m_forwardMode; 189 m_throwableResourceUri = base.m_throwableResourceUri; 190 m_redirectInfo = base.m_redirectInfo; 191 } 192 193 /** 194 * Default constructor.<p> 195 * 196 * @param cms the initial CmsObject to wrap in the controller 197 * @param resource the file requested 198 * @param cache the instance of the flex cache 199 * @param req the current request 200 * @param res the current response 201 * @param streaming indicates if the response is streaming 202 * @param top indicates if the response is the top response 203 */ 204 public CmsFlexController( 205 CmsObject cms, 206 CmsResource resource, 207 CmsFlexCache cache, 208 HttpServletRequest req, 209 HttpServletResponse res, 210 boolean streaming, 211 boolean top) { 212 213 m_cmsObject = cms; 214 m_resource = resource; 215 m_cache = cache; 216 m_req = req; 217 m_res = res; 218 m_streaming = streaming; 219 m_top = top; 220 m_flexRequestList = new Vector<CmsFlexRequest>(); 221 m_flexResponseList = new Vector<CmsFlexResponse>(); 222 m_flexContextInfoList = new Vector<CmsFlexRequestContextInfo>(); 223 m_forwardMode = false; 224 m_throwableResourceUri = null; 225 } 226 227 /** 228 * Returns the wrapped CmsObject form the provided request, or <code>null</code> if the 229 * request is not running inside OpenCms.<p> 230 * 231 * @param req the current request 232 * @return the wrapped CmsObject 233 */ 234 public static CmsObject getCmsObject(ServletRequest req) { 235 236 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 237 if (controller != null) { 238 return controller.getCmsObject(); 239 } else { 240 return null; 241 } 242 } 243 244 /** 245 * Returns the controller from the given request, or <code>null</code> if the 246 * request is not running inside OpenCms.<p> 247 * 248 * @param req the request to get the controller from 249 * 250 * @return the controller from the given request, or <code>null</code> if the request is not running inside OpenCms 251 */ 252 public static CmsFlexController getController(ServletRequest req) { 253 254 return (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 255 } 256 257 /** 258 * Provides access to a root cause Exception that might have occurred in a complex include scenario.<p> 259 * 260 * @param req the current request 261 * 262 * @return the root cause exception or null if no root cause exception is available 263 * 264 * @see #getThrowable() 265 */ 266 public static Throwable getThrowable(ServletRequest req) { 267 268 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 269 if (controller != null) { 270 return controller.getThrowable(); 271 } else { 272 return null; 273 } 274 } 275 276 /** 277 * Provides access to URI of a VFS resource that caused an exception that might have occurred in a complex include scenario.<p> 278 * 279 * @param req the current request 280 * 281 * @return to URI of a VFS resource that caused an exception, or <code>null</code> 282 * 283 * @see #getThrowableResourceUri() 284 */ 285 public static String getThrowableResourceUri(ServletRequest req) { 286 287 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 288 if (controller != null) { 289 return controller.getThrowableResourceUri(); 290 } else { 291 return null; 292 } 293 } 294 295 /** 296 * Checks if the provided request is running in OpenCms and the current users project is the online project.<p> 297 * 298 * @param req the current request 299 * 300 * @return <code>true</code> if the request is running in OpenCms and the current users project is 301 * the online project, <code>false</code> otherwise 302 */ 303 public static boolean isCmsOnlineRequest(ServletRequest req) { 304 305 if (req == null) { 306 return false; 307 } 308 return getController(req).getCmsObject().getRequestContext().getCurrentProject().isOnlineProject(); 309 } 310 311 /** 312 * Checks if the provided request is running in OpenCms.<p> 313 * 314 * @param req the current request 315 * 316 * @return <code>true</code> if the request is running in OpenCms, <code>false</code> otherwise 317 */ 318 public static boolean isCmsRequest(ServletRequest req) { 319 320 return ((req != null) && (req.getAttribute(ATTRIBUTE_NAME) != null)); 321 } 322 323 /** 324 * Checks if the request has the "If-Modified-Since" header set, and if so, 325 * if the header date value is equal to the provided last modification date.<p> 326 * 327 * @param req the request to set the "If-Modified-Since" date header from 328 * @param dateLastModified the date to compare the header with 329 * 330 * @return <code>true</code> if the header is set and the header date is equal to the provided date 331 */ 332 public static boolean isNotModifiedSince(HttpServletRequest req, long dateLastModified) { 333 334 // check if the request contains a last modified header 335 try { 336 long lastModifiedHeader = req.getDateHeader(CmsRequestUtil.HEADER_IF_MODIFIED_SINCE); 337 // if last modified header is set (> -1), compare it to the requested resource 338 return ((lastModifiedHeader > -1) && (((dateLastModified / 1000) * 1000) == lastModifiedHeader)); 339 } catch (Exception ex) { 340 // some clients (e.g. User-Agent: BlackBerry7290/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/111) 341 // send an invalid "If-Modified-Since" header (e.g. in german locale) 342 // which breaks with http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html 343 // this has to be caught because the subsequent request for the 500 error handler 344 // would run into the same exception. 345 LOG.warn( 346 Messages.get().getBundle().key( 347 Messages.ERR_HEADER_IFMODIFIEDSINCE_FORMAT_3, 348 new Object[] { 349 CmsRequestUtil.HEADER_IF_MODIFIED_SINCE, 350 req.getHeader(CmsRequestUtil.HEADER_USER_AGENT), 351 req.getHeader(CmsRequestUtil.HEADER_IF_MODIFIED_SINCE)})); 352 } 353 return false; 354 } 355 356 /** 357 * Tells the flex controller to never cache the given attribute.<p> 358 * 359 * @param attributeName the attribute which shouldn't be cached 360 */ 361 public static void registerUncacheableAttribute(String attributeName) { 362 363 uncacheableAttributes.add(attributeName); 364 } 365 366 /** 367 * Removes the controller attribute from a request.<p> 368 * 369 * @param req the request to remove the controller from 370 */ 371 public static void removeController(ServletRequest req) { 372 373 CmsFlexController controller = (CmsFlexController)req.getAttribute(ATTRIBUTE_NAME); 374 if (controller != null) { 375 controller.clear(); 376 } 377 } 378 379 /** 380 * Stores the given controller in the given request (using a request attribute).<p> 381 * 382 * @param req the request where to store the controller in 383 * @param controller the controller to store 384 */ 385 public static void setController(ServletRequest req, CmsFlexController controller) { 386 387 req.setAttribute(CmsFlexController.ATTRIBUTE_NAME, controller); 388 } 389 390 /** 391 * Sets the <code>Expires</code> date header for a given http request.<p> 392 * 393 * Also sets the <code>cache-control: max-age</code> header to the time of the expiration. 394 * A certain upper limit is imposed on the expiration date parameter to ensure the resources are 395 * not cached to long in proxies. This can be controlled by the <code>maxAge</code> parameter. 396 * If <code>maxAge</code> is lower then 0, then a default max age of 86400000 msec (1 day) is used.<p> 397 * 398 * @param res the response to set the "Expires" date header for 399 * @param maxAge maximum amount of time in milliseconds the response remains valid 400 * @param dateExpires the date to set (if this is not in the future, it is ignored) 401 */ 402 public static void setDateExpiresHeader(HttpServletResponse res, long dateExpires, long maxAge) { 403 404 long now = System.currentTimeMillis(); 405 if ((dateExpires > now) && (dateExpires != CmsResource.DATE_EXPIRED_DEFAULT)) { 406 407 // important: many caches (browsers or proxy) use the "Expires" header 408 // to avoid re-loading of pages that are not expired 409 // while this is right in general, no changes before the expiration date 410 // will be displayed 411 // therefore it is better to not use an expiration to far in the future 412 413 // if no valid max age is set, restrict it to 24 hrs 414 if (maxAge < 0L) { 415 maxAge = 86400000; 416 } 417 418 if ((dateExpires - now) > maxAge) { 419 // set "Expires" header max one day into the future 420 dateExpires = now + maxAge; 421 } 422 res.setDateHeader(CmsRequestUtil.HEADER_EXPIRES, dateExpires); 423 424 // setting the "Expires" header only is not sufficient - even expired documents seems to be cached 425 // therefore, the "cache-control: max-age" is also set 426 res.setHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, CmsRequestUtil.HEADER_VALUE_MAX_AGE + (maxAge / 1000L)); 427 } 428 } 429 430 /** 431 * Sets the "last modified" date header for a given http request.<p> 432 * 433 * @param res the response to set the "last modified" date header for 434 * @param dateLastModified the date to set (if this is lower then 0, the current time is set) 435 */ 436 public static void setDateLastModifiedHeader(HttpServletResponse res, long dateLastModified) { 437 438 if (dateLastModified > -1) { 439 // set date last modified header (precision is only second, not millisecond 440 res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, (dateLastModified / 1000) * 1000); 441 } else { 442 // this resource can not be optimized for "last modified", use current time as header 443 res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, System.currentTimeMillis()); 444 // avoiding issues with IE8+ 445 res.addHeader(CmsRequestUtil.HEADER_CACHE_CONTROL, "public, max-age=0"); 446 } 447 } 448 449 /** 450 * Clears all data of this controller.<p> 451 */ 452 public void clear() { 453 454 if (m_flexRequestList != null) { 455 m_flexRequestList.clear(); 456 } 457 m_flexRequestList = null; 458 if (m_flexResponseList != null) { 459 m_flexResponseList.clear(); 460 } 461 m_flexResponseList = null; 462 if (m_req != null) { 463 m_req.removeAttribute(ATTRIBUTE_NAME); 464 } 465 m_req = null; 466 m_res = null; 467 m_cmsObject = null; 468 m_resource = null; 469 m_cache = null; 470 m_throwable = null; 471 } 472 473 /** 474 * Returns the CmsFlexCache instance where all results from this request will be cached in.<p> 475 * 476 * This is public so that pages like the Flex Cache Administration page 477 * have a way to access the cache object.<p> 478 * 479 * @return the CmsFlexCache instance where all results from this request will be cached in 480 */ 481 public CmsFlexCache getCmsCache() { 482 483 return m_cache; 484 } 485 486 /** 487 * Returns the wrapped CmsObject.<p> 488 * 489 * @return the wrapped CmsObject 490 */ 491 public CmsObject getCmsObject() { 492 493 return m_cmsObject; 494 } 495 496 /** 497 * This method provides access to the top-level CmsResource of the request 498 * which is of a type that supports the FlexCache, 499 * i.e. usually the CmsFile that is identical to the file uri requested by the user, 500 * not he current included element.<p> 501 * 502 * @return the requested top-level CmsFile 503 */ 504 public CmsResource getCmsResource() { 505 506 return m_resource; 507 } 508 509 /** 510 * Returns the current flex request.<p> 511 * 512 * @return the current flex request 513 */ 514 public CmsFlexRequest getCurrentRequest() { 515 516 return m_flexRequestList.get(m_flexRequestList.size() - 1); 517 } 518 519 /** 520 * Returns the current flex response.<p> 521 * 522 * @return the current flex response 523 */ 524 public CmsFlexResponse getCurrentResponse() { 525 526 return m_flexResponseList.get(m_flexResponseList.size() - 1); 527 } 528 529 /** 530 * Returns the combined "expires" date for all resources read during this request.<p> 531 * 532 * @return the combined "expires" date for all resources read during this request 533 */ 534 public long getDateExpires() { 535 536 int pos = m_flexContextInfoList.size() - 1; 537 if (pos < 0) { 538 // ensure a valid position is used 539 return CmsResource.DATE_EXPIRED_DEFAULT; 540 } 541 return (m_flexContextInfoList.get(pos)).getDateExpires(); 542 } 543 544 /** 545 * Returns the combined "last modified" date for all resources read during this request.<p> 546 * 547 * @return the combined "last modified" date for all resources read during this request 548 */ 549 public long getDateLastModified() { 550 551 int pos = m_flexContextInfoList.size() - 1; 552 if (pos < 0) { 553 // ensure a valid position is used 554 return CmsResource.DATE_RELEASED_DEFAULT; 555 } 556 return (m_flexContextInfoList.get(pos)).getDateLastModified(); 557 } 558 559 /** 560 * Gets the information about where to redirect to. 561 * 562 * @return the redirect information 563 */ 564 public RedirectInfo getRedirectInfo() { 565 566 return m_redirectInfo; 567 } 568 569 /** 570 * Returns the size of the response stack.<p> 571 * 572 * @return the size of the response stack 573 */ 574 public int getResponseStackSize() { 575 576 return m_flexResponseList.size(); 577 } 578 579 /** 580 * Returns an exception (Throwable) that was caught during inclusion of sub elements, 581 * or null if no exceptions where thrown in sub elements.<p> 582 * 583 * @return an exception (Throwable) that was caught during inclusion of sub elements 584 */ 585 public Throwable getThrowable() { 586 587 return m_throwable; 588 } 589 590 /** 591 * Returns the URI of a VFS resource that caused the exception that was caught during inclusion of sub elements, 592 * might return null if no URI information was available for the exception.<p> 593 * 594 * @return the URI of a VFS resource that caused the exception that was caught during inclusion of sub elements 595 */ 596 public String getThrowableResourceUri() { 597 598 return m_throwableResourceUri; 599 } 600 601 /** 602 * Returns the current http request.<p> 603 * 604 * @return the current http request 605 */ 606 public HttpServletRequest getTopRequest() { 607 608 return m_req; 609 } 610 611 /** 612 * Returns the current http response.<p> 613 * 614 * @return the current http response 615 */ 616 public HttpServletResponse getTopResponse() { 617 618 return m_res; 619 } 620 621 /** 622 * Returns <code>true</code> if the controller does not yet contain any requests.<p> 623 * 624 * @return <code>true</code> if the controller does not yet contain any requests 625 */ 626 public boolean isEmptyRequestList() { 627 628 return (m_flexRequestList != null) && m_flexRequestList.isEmpty(); 629 } 630 631 /** 632 * Returns <code>true</code> if this controller is currently in "forward" mode.<p> 633 * 634 * @return <code>true</code> if this controller is currently in "forward" mode 635 */ 636 public boolean isForwardMode() { 637 638 return m_forwardMode; 639 } 640 641 /** 642 * Returns <code>true</code> if the generated output of the response should 643 * be written to the stream directly.<p> 644 * 645 * @return <code>true</code> if the generated output of the response should be written to the stream directly 646 */ 647 public boolean isStreaming() { 648 649 return m_streaming; 650 } 651 652 /** 653 * Returns <code>true</code> if this controller was generated as top level controller.<p> 654 * 655 * If a resource (e.g. a JSP) is processed and it's content is included in 656 * another resource, then this will be <code>false</code>. 657 * 658 * @return <code>true</code> if this controller was generated as top level controller 659 * 660 * @see org.opencms.loader.I_CmsResourceLoader#dump(CmsObject, CmsResource, String, java.util.Locale, HttpServletRequest, HttpServletResponse) 661 * @see org.opencms.jsp.CmsJspActionElement#getContent(String) 662 */ 663 public boolean isTop() { 664 665 return m_top; 666 } 667 668 /** 669 * Removes the topmost request/response pair from the stack.<p> 670 */ 671 public void pop() { 672 673 if ((m_flexRequestList != null) && !m_flexRequestList.isEmpty()) { 674 m_flexRequestList.remove(m_flexRequestList.size() - 1); 675 } 676 if ((m_flexResponseList != null) && !m_flexRequestList.isEmpty()) { 677 m_flexResponseList.remove(m_flexResponseList.size() - 1); 678 } 679 if ((m_flexContextInfoList != null) && !m_flexContextInfoList.isEmpty()) { 680 CmsFlexRequestContextInfo info = m_flexContextInfoList.remove(m_flexContextInfoList.size() - 1); 681 if (m_flexContextInfoList.size() > 0) { 682 (m_flexContextInfoList.get(0)).merge(info); 683 updateRequestContextInfo(); 684 } 685 } 686 } 687 688 /** 689 * Adds another flex request/response pair to the stack.<p> 690 * 691 * @param req the request to add 692 * @param res the response to add 693 */ 694 public void push(CmsFlexRequest req, CmsFlexResponse res) { 695 696 m_flexRequestList.add(req); 697 m_flexResponseList.add(res); 698 m_flexContextInfoList.add(new CmsFlexRequestContextInfo()); 699 updateRequestContextInfo(); 700 } 701 702 /** 703 * Removes request attributes which shouldn't be cached in flex cache entries from a map.<p> 704 * 705 * @param attributeMap the map of attributes 706 */ 707 public void removeUncacheableAttributes(Map<String, Object> attributeMap) { 708 709 for (String uncacheableAttribute : uncacheableAttributes) { 710 attributeMap.remove(uncacheableAttribute); 711 } 712 attributeMap.remove(CmsFlexController.ATTRIBUTE_NAME); 713 attributeMap.remove(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE); 714 attributeMap.remove(CmsDetailPageResourceHandler.ATTR_DETAIL_FUNCTION_PAGE); 715 } 716 717 /** 718 * Sets the value of the "forward mode" flag.<p> 719 * 720 * @param value the forward mode to set 721 */ 722 public void setForwardMode(boolean value) { 723 724 m_forwardMode = value; 725 } 726 727 /** 728 * Sets the information about where to redirect to. 729 * 730 * @param redirectInfo the redirect information 731 */ 732 public void setRedirectInfo(RedirectInfo redirectInfo) { 733 734 m_redirectInfo = redirectInfo; 735 } 736 737 /** 738 * Sets an exception (Throwable) that was caught during inclusion of sub elements.<p> 739 * 740 * If another exception is already set in this controller, then the additional exception 741 * is ignored.<p> 742 * 743 * @param throwable the exception (Throwable) to set 744 * @param resource the URI of the VFS resource the error occurred on (might be <code>null</code> if unknown) 745 * 746 * @return the exception stored in the controller 747 */ 748 public Throwable setThrowable(Throwable throwable, String resource) { 749 750 if (m_throwable == null) { 751 m_throwable = throwable; 752 m_throwableResourceUri = resource; 753 } else { 754 if (LOG.isDebugEnabled()) { 755 if (resource != null) { 756 LOG.debug( 757 Messages.get().getBundle().key(Messages.LOG_FLEXCONTROLLER_IGNORED_EXCEPTION_1, resource)); 758 } else { 759 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCONTROLLER_IGNORED_EXCEPTION_0)); 760 } 761 } 762 } 763 return m_throwable; 764 } 765 766 /** 767 * Puts the response in a suspended state.<p> 768 */ 769 public void suspendFlexResponse() { 770 771 for (int i = 0; i < m_flexResponseList.size(); i++) { 772 CmsFlexResponse res = m_flexResponseList.get(i); 773 res.setSuspended(true); 774 } 775 } 776 777 /** 778 * Updates the "last modified" date and the "expires" date 779 * for all resources read during this request with the given values.<p> 780 * 781 * The currently stored value for "last modified" is only updated with the new value if 782 * the new value is either larger (i.e. newer) then the stored value, 783 * or if the new value is less then zero, which indicates that the "last modified" 784 * optimization can not be used because the element is dynamic.<p> 785 * 786 * The stored "expires" value is only updated if the new value is smaller 787 * then the stored value.<p> 788 * 789 * @param dateLastModified the value to update the "last modified" date with 790 * @param dateExpires the value to update the "expires" date with 791 */ 792 public void updateDates(long dateLastModified, long dateExpires) { 793 794 int pos = m_flexContextInfoList.size() - 1; 795 if (pos < 0) { 796 // ensure a valid position is used 797 return; 798 } 799 (m_flexContextInfoList.get(pos)).updateDates(dateLastModified, dateExpires); 800 } 801 802 /** 803 * Updates the context info of the request context.<p> 804 */ 805 private void updateRequestContextInfo() { 806 807 if ((m_flexContextInfoList != null) && !m_flexContextInfoList.isEmpty()) { 808 m_cmsObject.getRequestContext().setAttribute( 809 CmsRequestUtil.HEADER_LAST_MODIFIED, 810 m_flexContextInfoList.get(m_flexContextInfoList.size() - 1)); 811 } 812 } 813}