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