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