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.flex.CmsFlexRequestKey.PathsBean; 031import org.opencms.loader.I_CmsResourceLoader; 032import org.opencms.main.CmsLog; 033import org.opencms.util.CmsStringUtil; 034 035import java.util.Arrays; 036import java.util.Collections; 037import java.util.HashSet; 038import java.util.Iterator; 039import java.util.List; 040import java.util.Map; 041import java.util.Set; 042 043import javax.servlet.http.HttpSession; 044 045import org.apache.commons.logging.Log; 046 047import com.google.common.collect.Lists; 048 049/** 050 * Key used to describe the caching behaviour of a specific resource.<p> 051 * 052 * It has a lot of variables that are directly accessed (which isn't good style, I know) 053 * to avoid method calling overhead (a cache is about speed, isn't it :).<p> 054 * 055 * @since 6.0.0 056 */ 057public class CmsFlexCacheKey { 058 059 /** Flex cache keyword: always. */ 060 private static final String CACHE_00_ALWAYS = "always"; 061 062 /** Flex cache keyword: never. */ 063 private static final String CACHE_01_NEVER = "never"; 064 065 /** Flex cache keyword: uri. */ 066 private static final String CACHE_02_URI = "uri"; 067 068 /** Flex cache keyword: user. */ 069 private static final String CACHE_03_USER = "user"; 070 071 /** Flex cache keyword: params. */ 072 private static final String CACHE_04_PARAMS = "params"; 073 074 /** Flex cache keyword: no-params. */ 075 private static final String CACHE_05_NO_PARAMS = "no-params"; 076 077 /** Flex cache keyword: timeout. */ 078 private static final String CACHE_06_TIMEOUT = "timeout"; 079 080 /** Flex cache keyword: session. */ 081 private static final String CACHE_07_SESSION = "session"; 082 083 /** Flex cache keyword: schemes. */ 084 private static final String CACHE_08_SCHEMES = "schemes"; 085 086 /** Flex cache keyword: ports. */ 087 private static final String CACHE_09_PORTS = "ports"; 088 089 /** Flex cache keyword: false. */ 090 private static final String CACHE_10_FALSE = CmsStringUtil.FALSE; 091 092 /** Flex cache keyword: parse-error. */ 093 private static final String CACHE_11_PARSE_ERROR = "parse-error"; 094 095 /** Flex cache keyword: true. */ 096 private static final String CACHE_12_TRUE = CmsStringUtil.TRUE; 097 098 /** Flex cache keyword: ip. */ 099 private static final String CACHE_13_IP = "ip"; 100 101 /** Flex cache keyword: element. */ 102 private static final String CACHE_14_ELEMENT = "element"; 103 104 /** Flex cache keyword: locale. */ 105 private static final String CACHE_15_LOCALE = "locale"; 106 107 /** Flex cache keyword: encoding. */ 108 private static final String CACHE_16_ENCODING = "encoding"; 109 110 /** Flex cache keyword: site. */ 111 private static final String CACHE_17_SITE = "site"; 112 113 /** Flex cache keyword: attrs. */ 114 private static final String CACHE_18_ATTRS = "attrs"; 115 116 /** Flex cache keyword: no-attrs. */ 117 private static final String CACHE_19_NO_ATTRS = "no-attrs"; 118 119 /** Flex cache keyword: device. */ 120 private static final String CACHE_20_DEVICE = "device"; 121 122 /** Flex cache keyword: container-element. */ 123 private static final String CACHE_21_CONTAINER_ELEMENT = "container-element"; 124 125 /** The list of keywords of the Flex cache language. */ 126 private static final List<String> CACHE_COMMANDS = Arrays.asList( 127 new String[] { 128 CACHE_00_ALWAYS, 129 CACHE_01_NEVER, 130 CACHE_02_URI, 131 CACHE_03_USER, 132 CACHE_04_PARAMS, 133 CACHE_05_NO_PARAMS, 134 CACHE_06_TIMEOUT, 135 CACHE_07_SESSION, 136 CACHE_08_SCHEMES, 137 CACHE_09_PORTS, 138 CACHE_10_FALSE, 139 CACHE_11_PARSE_ERROR, 140 CACHE_12_TRUE, 141 CACHE_13_IP, 142 CACHE_14_ELEMENT, 143 CACHE_15_LOCALE, 144 CACHE_16_ENCODING, 145 CACHE_17_SITE, 146 CACHE_18_ATTRS, 147 CACHE_19_NO_ATTRS, 148 CACHE_20_DEVICE, 149 CACHE_21_CONTAINER_ELEMENT}); 150 151 /** Marker to identify use of certain String key members (uri, ip etc.). */ 152 private static final String IS_USED = "/ /"; 153 154 /** The log object for this class. */ 155 private static final Log LOG = CmsLog.getLog(CmsFlexCacheKey.class); 156 157 /** Cache key variable: Determines if this resource can be cached alwys, never or under certain conditions. -1 = never, 0=check, 1=always. */ 158 private int m_always; 159 160 /** Cache key variable: List of attributes. */ 161 private Set<String> m_attrs; 162 163 /** Cache key variable: The current container element. */ 164 private String m_containerElement; 165 166 /** Cache key variable: The current device. */ 167 private String m_device; 168 169 /** Cache key variable: The requested element. */ 170 private String m_element; 171 172 /** Cache key variable: The requested encoding. */ 173 private String m_encoding; 174 175 /** Cache key variable: The ip address of the request. */ 176 private String m_ip; 177 178 /** Cache key variable: The requested locale. */ 179 private String m_locale; 180 181 /** Cache key variable: List of "blocking" attributes. */ 182 private Set<String> m_noattrs; 183 184 /** Cache key variable: List of "blocking" parameters. */ 185 private Set<String> m_noparams; 186 187 /** Cache key variable: List of parameters. */ 188 private Set<String> m_params; 189 190 /** Flag raised in case a key parse error occurred. */ 191 private boolean m_parseError; 192 193 /** Cache key variable: The request TCP/IP port. */ 194 private Set<Integer> m_ports; 195 196 /** The OpenCms resource that this key is used for. */ 197 private String m_resource; 198 199 /** Cache key variable: Distinguishes request schemes (http, https etc.). */ 200 private Set<String> m_schemes; 201 202 /** Cache key variable: List of session variables. */ 203 private Set<String> m_session; 204 205 /** Cache key variable: The current site root. */ 206 private String m_site; 207 208 /** Cache key variable: Timeout of the resource. */ 209 private long m_timeout; 210 211 /** Cache key variable: The uri of the original request. */ 212 private String m_uri; 213 214 /** Cache key variable: The user id. */ 215 private String m_user; 216 217 /** The cache behaviour description for the resource. */ 218 private String m_variation; 219 220 /** Resource without online / offline suffix. */ 221 private String m_actualResource; 222 223 /** 224 * This constructor is used when building a cache key from set of cache directives.<p> 225 * 226 * These directives are attached to the properties of the requested resource 227 * on a property called "cache". 228 * The value of this poperty that is passed in this constructor as "cacheDirectives" 229 * is parsed to build the keys data structure.<p> 230 * 231 * In case a parsing error occures, the value of this key is set to "cache=never", 232 * and the hadParseError() flag is set to true. 233 * This is done to ensure that a valid key is always constructed with the constructor.<p> 234 * 235 * @param resourcename the full name of the resource including site root 236 * @param cacheDirectives the cache directives of the resource (value of the property "cache") 237 * @param online must be true for an online resource, false for offline resources 238 */ 239 public CmsFlexCacheKey(String resourcename, String cacheDirectives, boolean online) { 240 241 m_actualResource = resourcename; 242 m_resource = getKeyName(resourcename, online); 243 m_variation = "never"; 244 m_always = -1; 245 m_timeout = -1; 246 if (cacheDirectives != null) { 247 parseFlexKey(cacheDirectives); 248 } 249 if (LOG.isDebugEnabled()) { 250 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_GENERATED_1, toString())); 251 } 252 } 253 254 /** 255 * Calculates the cache key name that is used as key in 256 * the first level of the FlexCache.<p> 257 * 258 * @param resourcename the full name of the resource including site root 259 * @param online must be true for an online resource, false for offline resources 260 * 261 * @return the FlexCache key name 262 */ 263 public static String getKeyName(String resourcename, boolean online) { 264 265 return resourcename.concat(online ? CmsFlexCache.CACHE_ONLINESUFFIX : CmsFlexCache.CACHE_OFFLINESUFFIX); 266 } 267 268 /** 269 * Returns resource name from given key name.<p> 270 * 271 * @param keyName given name of key. 272 * @return name of resource if key is valid, otherwise "" 273 */ 274 public static String getResourceName(String keyName) { 275 276 if (keyName.endsWith(CmsFlexCache.CACHE_OFFLINESUFFIX) | keyName.endsWith(CmsFlexCache.CACHE_ONLINESUFFIX)) { 277 return keyName.split(" ")[0]; 278 } else { 279 return ""; 280 } 281 } 282 283 /** 284 * Appends a flex cache key value to the given buffer.<p> 285 * 286 * @param str the buffer to append to 287 * @param key the key to append 288 * @param value the value to append 289 */ 290 private static void appendKeyValue(StringBuffer str, String key, String value) { 291 292 str.append(key); 293 if (value == IS_USED) { 294 str.append(";"); 295 } else { 296 str.append("=("); 297 str.append(value); 298 str.append(");"); 299 } 300 } 301 302 /** 303 * Returns the actual resource path under which this is cached, without online / offline suffix.<p> 304 * 305 * @return the actual resource path 306 */ 307 public String getActualResource() { 308 309 return m_actualResource; 310 } 311 312 /** 313 * Gets the list of root paths for the cache key / request key combination which should be used to determine the set of flex cache buckets for the flex cache entry.<p> 314 * 315 * @param key the flex request key 316 * @return the list of paths which should be used to determine the flex cache buckets 317 */ 318 public List<String> getPathsForBuckets(CmsFlexRequestKey key) { 319 320 PathsBean pathBean = key.getPaths(); 321 List<String> paths = Lists.newArrayList(); 322 if (m_uri != null) { 323 paths.add(pathBean.getUri()); 324 paths.add(pathBean.getDetailElement()); 325 } 326 if (m_site != null) { 327 paths.add(pathBean.getSite()); 328 } 329 if (m_containerElement != null) { 330 paths.add(pathBean.getContainerElement()); 331 } 332 333 paths.removeAll(Collections.singletonList(null)); 334 return paths; 335 } 336 337 /** 338 * This flag is used to indicate that a parse error had 339 * occurred, which can happen if the cache directives String 340 * passed to the constructor using the response is 341 * not build according to the Flex cache language syntax.<p> 342 * 343 * @return true if a parse error did occur, false otherwise 344 */ 345 public boolean hadParseError() { 346 347 return m_parseError; 348 } 349 350 /** 351 * Compares this key to the other key passed as parameter, 352 * from comparing the two keys, a variation String is constructed.<p> 353 * 354 * This method is the "heart" of the key matching process.<p> 355 * 356 * The assumtion is that this key should be the one constructed for the response, 357 * while the parameter key should have been constructed from the request.<p> 358 * 359 * A short example how this works: 360 * If the cache key is "cache=user" and the request is done from a guest user 361 * the constructed variation will be "user=(guest)".<p> 362 * 363 * @param key the key to match this key with 364 * @return null if not cachable, or the Variation String if cachable 365 */ 366 public String matchRequestKey(CmsFlexRequestKey key) { 367 368 StringBuffer str = new StringBuffer(100); 369 if (m_always < 0) { 370 if (LOG.isDebugEnabled()) { 371 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_KEYMATCH_CACHE_NEVER_0)); 372 } 373 return null; 374 } 375 376 if (LOG.isDebugEnabled()) { 377 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_KEYMATCH_CHECK_NO_PARAMS_0)); 378 } 379 if ((m_noparams != null) && (key.getParams() != null)) { 380 if ((m_noparams.size() == 0) && (key.getParams().size() > 0)) { 381 return null; 382 } 383 Iterator<String> i = key.getParams().keySet().iterator(); 384 while (i.hasNext()) { 385 if (m_noparams.contains(i.next())) { 386 return null; 387 } 388 } 389 } 390 391 if (LOG.isDebugEnabled()) { 392 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_KEYMATCH_CHECK_NO_ATTRS_0)); 393 } 394 if ((m_noattrs != null) && (key.getAttributes() != null)) { 395 if ((m_noattrs.size() == 0) && (key.getAttributes().size() > 0)) { 396 return null; 397 } 398 Iterator<String> i = key.getAttributes().keySet().iterator(); 399 while (i.hasNext()) { 400 if (m_noattrs.contains(i.next())) { 401 return null; 402 } 403 } 404 } 405 406 if (m_always > 0) { 407 if (LOG.isDebugEnabled()) { 408 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_KEYMATCH_CACHE_ALWAYS_0)); 409 } 410 str.append(CACHE_00_ALWAYS); 411 return str.toString(); 412 } 413 414 if (m_uri != null) { 415 appendKeyValue(str, CACHE_02_URI, key.getUri()); 416 } 417 418 if (m_site != null) { 419 appendKeyValue(str, CACHE_17_SITE, key.getSite()); 420 } 421 422 if (m_element != null) { 423 appendKeyValue(str, CACHE_14_ELEMENT, key.getElement()); 424 } 425 426 if (m_device != null) { 427 appendKeyValue(str, CACHE_20_DEVICE, key.getDevice()); 428 } 429 430 if (m_containerElement != null) { 431 appendKeyValue(str, CACHE_21_CONTAINER_ELEMENT, key.getContainerElement()); 432 } 433 434 if (m_locale != null) { 435 appendKeyValue(str, CACHE_15_LOCALE, key.getLocale()); 436 } 437 438 if (m_encoding != null) { 439 appendKeyValue(str, CACHE_16_ENCODING, key.getEncoding()); 440 } 441 442 if (m_ip != null) { 443 appendKeyValue(str, CACHE_13_IP, key.getIp()); 444 } 445 446 if (m_user != null) { 447 appendKeyValue(str, CACHE_03_USER, key.getUser()); 448 } 449 450 if (m_params != null) { 451 str.append(CACHE_04_PARAMS); 452 str.append("=("); 453 Map<String, String[]> keyParams = key.getParams(); 454 if (keyParams != null) { 455 if (m_params.size() > 0) { 456 // match only params listed in cache directives 457 Iterator<String> i = m_params.iterator(); 458 while (i.hasNext()) { 459 Object o = i.next(); 460 if (keyParams.containsKey(o)) { 461 str.append(o); 462 str.append("="); 463 // TODO: handle multiple occurrences of the same parameter value 464 String[] values = keyParams.get(o); 465 str.append(values[0]); 466 if (i.hasNext()) { 467 str.append(","); 468 } 469 } 470 } 471 } else { 472 // match all request params 473 Iterator<Map.Entry<String, String[]>> i = keyParams.entrySet().iterator(); 474 while (i.hasNext()) { 475 Map.Entry<String, String[]> entry = i.next(); 476 str.append(entry.getKey()); 477 str.append("="); 478 // TODO: handle multiple occurrences of the same parameter value 479 String[] values = entry.getValue(); 480 str.append(values[0]); 481 if (i.hasNext()) { 482 str.append(","); 483 } 484 } 485 } 486 } 487 str.append(");"); 488 } 489 490 if (m_attrs != null) { 491 str.append(CACHE_18_ATTRS); 492 str.append("=("); 493 Map<String, Object> keyAttrs = key.getAttributes(); 494 if (keyAttrs != null) { 495 if (m_attrs.size() > 0) { 496 // match only attributes listed in cache directives 497 Iterator<String> i = m_attrs.iterator(); 498 while (i.hasNext()) { 499 String s = i.next(); 500 if (keyAttrs.containsKey(s)) { 501 str.append(s); 502 str.append("="); 503 Object value = keyAttrs.get(s); 504 str.append(value); 505 if (i.hasNext()) { 506 str.append(","); 507 } 508 } 509 } 510 } else { 511 // match all request attributes 512 Iterator<Map.Entry<String, Object>> i = keyAttrs.entrySet().iterator(); 513 while (i.hasNext()) { 514 Map.Entry<String, Object> entry = i.next(); 515 str.append(entry.getKey()); 516 str.append("="); 517 Object value = entry.getValue(); 518 str.append(value); 519 if (i.hasNext()) { 520 str.append(","); 521 } 522 } 523 } 524 } 525 str.append(");"); 526 } 527 528 if (m_session != null) { 529 StringBuffer buf = new StringBuffer(32); 530 boolean found = false; 531 buf.append(CACHE_07_SESSION); 532 buf.append("=("); 533 HttpSession keySession = key.getSession(); 534 if (keySession != null) { 535 // match only session attributes listed in cache directives 536 Iterator<String> i = m_session.iterator(); 537 while (i.hasNext()) { 538 String name = i.next(); 539 Object val = keySession.getAttribute(name); 540 if (val != null) { 541 found = true; 542 buf.append(name); 543 buf.append("="); 544 buf.append(val); 545 if (i.hasNext()) { 546 buf.append(","); 547 } 548 } 549 } 550 } 551 if (found) { 552 buf.append(");"); 553 str.append(buf); 554 } 555 } 556 557 if (m_schemes != null) { 558 String s = key.getScheme(); 559 if ((m_schemes.size() > 0) && (!m_schemes.contains(s))) { 560 return null; 561 } 562 appendKeyValue(str, CACHE_08_SCHEMES, s); 563 } 564 565 if (m_ports != null) { 566 Integer i = key.getPort(); 567 if ((m_ports.size() > 0) && (!m_ports.contains(i))) { 568 return null; 569 } 570 str.append(CACHE_09_PORTS); 571 str.append("=("); 572 str.append(i); 573 str.append(");"); 574 } 575 576 if (m_timeout > 0) { 577 str.append(CACHE_06_TIMEOUT); 578 str.append("=("); 579 str.append(m_timeout); 580 str.append(");"); 581 } 582 583 if (str.length() > 0) { 584 return str.toString(); 585 } else { 586 return null; 587 } 588 } 589 590 /** 591 * @see java.lang.Object#toString() 592 * 593 * @return a complete String representation for this key 594 */ 595 @Override 596 public String toString() { 597 598 StringBuffer str = new StringBuffer(100); 599 600 if (m_always < 0) { 601 str.append(CACHE_01_NEVER); 602 if (m_parseError) { 603 str.append(";"); 604 str.append(CACHE_11_PARSE_ERROR); 605 } 606 return str.toString(); 607 } 608 if (m_noparams != null) { 609 // add "no-cachable" parameters 610 str.append(CACHE_05_NO_PARAMS); 611 if (m_noparams.size() == 0) { 612 str.append(";"); 613 } else { 614 str.append("=("); 615 Iterator<String> i = m_noparams.iterator(); 616 while (i.hasNext()) { 617 Object o = i.next(); 618 str.append(o); 619 if (i.hasNext()) { 620 str.append(","); 621 } 622 } 623 str.append(");"); 624 } 625 } 626 if (m_noattrs != null) { 627 // add "no-cachable" attributes 628 str.append(CACHE_19_NO_ATTRS); 629 if (m_noattrs.size() == 0) { 630 str.append(";"); 631 } else { 632 str.append("=("); 633 Iterator<String> i = m_noattrs.iterator(); 634 while (i.hasNext()) { 635 String s = i.next(); 636 str.append(s); 637 if (i.hasNext()) { 638 str.append(","); 639 } 640 } 641 str.append(");"); 642 } 643 } 644 if (m_always > 0) { 645 str.append(CACHE_00_ALWAYS); 646 if (m_parseError) { 647 str.append(";"); 648 str.append(CACHE_11_PARSE_ERROR); 649 } 650 return str.toString(); 651 } 652 if (m_uri != null) { 653 // add uri 654 appendKeyValue(str, CACHE_02_URI, m_uri); 655 } 656 if (m_site != null) { 657 // add site 658 appendKeyValue(str, CACHE_17_SITE, m_site); 659 } 660 if (m_element != null) { 661 // add element 662 appendKeyValue(str, CACHE_14_ELEMENT, m_element); 663 } 664 if (m_device != null) { 665 appendKeyValue(str, CACHE_20_DEVICE, m_device); 666 } 667 if (m_containerElement != null) { 668 appendKeyValue(str, CACHE_21_CONTAINER_ELEMENT, m_containerElement); 669 } 670 if (m_locale != null) { 671 // add locale 672 appendKeyValue(str, CACHE_15_LOCALE, m_locale); 673 } 674 if (m_encoding != null) { 675 // add encoding 676 appendKeyValue(str, CACHE_16_ENCODING, m_encoding); 677 } 678 if (m_ip != null) { 679 // add ip 680 appendKeyValue(str, CACHE_13_IP, m_ip); 681 } 682 if (m_user != null) { 683 // add user 684 appendKeyValue(str, CACHE_03_USER, m_user); 685 } 686 if (m_params != null) { 687 // add parameters 688 str.append(CACHE_04_PARAMS); 689 if (m_params.size() == 0) { 690 str.append(";"); 691 } else { 692 str.append("=("); 693 Iterator<String> i = m_params.iterator(); 694 while (i.hasNext()) { 695 Object o = i.next(); 696 if (I_CmsResourceLoader.PARAMETER_ELEMENT.equals(o)) { 697 continue; 698 } 699 str.append(o); 700 if (i.hasNext()) { 701 str.append(","); 702 } 703 } 704 str.append(");"); 705 } 706 } 707 if (m_attrs != null) { 708 // add attributes 709 str.append(CACHE_18_ATTRS); 710 if (m_attrs.size() == 0) { 711 str.append(";"); 712 } else { 713 str.append("=("); 714 Iterator<String> i = m_attrs.iterator(); 715 while (i.hasNext()) { 716 String s = i.next(); 717 str.append(s); 718 if (i.hasNext()) { 719 str.append(","); 720 } 721 } 722 str.append(");"); 723 } 724 } 725 if (m_session != null) { 726 // add session variables 727 str.append(CACHE_07_SESSION); 728 str.append("=("); 729 Iterator<String> i = m_session.iterator(); 730 while (i.hasNext()) { 731 Object o = i.next(); 732 str.append(o); 733 if (i.hasNext()) { 734 str.append(","); 735 } 736 } 737 str.append(");"); 738 } 739 if (m_timeout >= 0) { 740 // add timeout 741 str.append(CACHE_06_TIMEOUT); 742 str.append("=("); 743 str.append(m_timeout); 744 str.append(");"); 745 } 746 if (m_schemes != null) { 747 // add schemes 748 str.append(CACHE_08_SCHEMES); 749 if (m_schemes.size() == 0) { 750 str.append(";"); 751 } else { 752 str.append("=("); 753 Iterator<String> i = m_schemes.iterator(); 754 while (i.hasNext()) { 755 str.append(i.next()); 756 if (i.hasNext()) { 757 str.append(","); 758 } 759 } 760 str.append(");"); 761 } 762 } 763 if (m_ports != null) { 764 // add ports 765 str.append(CACHE_09_PORTS); 766 if (m_ports.size() == 0) { 767 str.append(";"); 768 } else { 769 str.append("=("); 770 Iterator<Integer> i = m_ports.iterator(); 771 while (i.hasNext()) { 772 str.append(i.next()); 773 if (i.hasNext()) { 774 str.append(","); 775 } 776 } 777 str.append(");"); 778 } 779 } 780 781 if (m_parseError) { 782 str.append(CACHE_11_PARSE_ERROR); 783 } 784 return str.toString(); 785 } 786 787 /** 788 * Returns the resource.<p> 789 * 790 * @return the resource 791 */ 792 protected String getResource() { 793 794 return m_resource; 795 } 796 797 /** 798 * Returns the timeout.<p> 799 * 800 * @return the timeout 801 */ 802 protected long getTimeout() { 803 804 return m_timeout; 805 } 806 807 /** 808 * Returns the variation.<p> 809 * 810 * @return the variation 811 */ 812 protected String getVariation() { 813 814 return m_variation; 815 } 816 817 /** 818 * Sets the variation.<p> 819 * 820 * @param variation the variation to set 821 */ 822 protected void setVariation(String variation) { 823 824 m_variation = variation; 825 } 826 827 /** 828 * Parse a String in the Flex cache language and construct 829 * the key data structure from this.<p> 830 * 831 * @param key the String to parse (usually read from the file property "cache") 832 */ 833 private void parseFlexKey(String key) { 834 835 List<String> tokens = CmsStringUtil.splitAsList(key, ';', false); 836 Iterator<String> i = tokens.iterator(); 837 try { 838 while (i.hasNext()) { 839 String t = i.next(); 840 String k = null; 841 String v = null; 842 int idx = t.indexOf('='); 843 if (idx >= 0) { 844 k = t.substring(0, idx).trim(); 845 if (t.length() > idx) { 846 v = t.substring(idx + 1).trim(); 847 } 848 } else { 849 k = t.trim(); 850 } 851 m_always = 0; 852 if (LOG.isDebugEnabled()) { 853 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_PARSE_FLEXKEY_3, t, k, v)); 854 } 855 switch (CACHE_COMMANDS.indexOf(k)) { 856 case 0: // always 857 case 12: // true 858 m_always = 1; 859 // continue processing (make sure we find a "never" behind "always") 860 break; 861 case 1: // never 862 case 10: // false 863 m_always = -1; 864 // no need for any further processing 865 return; 866 case 2: // uri 867 m_uri = IS_USED; // marks m_uri as being used 868 break; 869 case 3: // user 870 m_user = IS_USED; // marks m_user as being used 871 break; 872 case 4: // params 873 if (v != null) { 874 m_params = parseValueList(v); 875 } else { 876 m_params = Collections.emptySet(); 877 } 878 879 if (m_params.contains(I_CmsResourceLoader.PARAMETER_ELEMENT)) { 880 // workaround for element setting by parameter in OpenCms < 6.0 881 m_element = IS_USED; 882 m_params.remove(I_CmsResourceLoader.PARAMETER_ELEMENT); 883 if (m_params.size() == 0) { 884 m_params = null; 885 } 886 } 887 break; 888 case 5: // no-params 889 if (v != null) { 890 // no-params are present 891 m_noparams = parseValueList(v); 892 } else { 893 // never cache with parameters 894 m_noparams = Collections.emptySet(); 895 } 896 break; 897 case 6: // timeout 898 m_timeout = Integer.parseInt(v); 899 break; 900 case 7: // session 901 m_session = parseValueList(v); 902 if (m_session.size() <= 0) { 903 // session must have at last one variable set 904 m_parseError = true; 905 } 906 break; 907 case 8: // schemes 908 m_schemes = parseValueList(v); 909 break; 910 case 9: // ports 911 Set<String> ports = parseValueList(v); 912 m_ports = new HashSet<Integer>(ports.size()); 913 for (String p : ports) { 914 try { 915 m_ports.add(Integer.valueOf(p)); 916 } catch (NumberFormatException e) { 917 // ignore this number 918 } 919 } 920 break; 921 case 11: // previous parse error - ignore 922 break; 923 case 13: // ip 924 m_ip = IS_USED; // marks ip as being used 925 break; 926 case 14: // element 927 m_element = IS_USED; 928 break; 929 case 15: // locale 930 m_locale = IS_USED; 931 break; 932 case 16: // encoding 933 m_encoding = IS_USED; 934 break; 935 case 17: // site 936 m_site = IS_USED; 937 break; 938 case 18: // attrs 939 if (v != null) { 940 m_attrs = parseValueList(v); 941 } else { 942 m_attrs = null; 943 } 944 break; 945 case 19: // no-attrs 946 if (v != null) { 947 // no-attrs are present 948 m_noattrs = parseValueList(v); 949 } else { 950 // never cache with attributes 951 m_noattrs = Collections.emptySet(); 952 } 953 break; 954 case 20: // device 955 m_device = IS_USED; // marks m_device as being used 956 break; 957 case 21: // container element 958 m_containerElement = IS_USED; 959 break; 960 default: // unknown directive, throw error 961 m_parseError = true; 962 } 963 } 964 } catch (Exception e) { 965 // any Exception here indicates a parsing error 966 if (LOG.isErrorEnabled()) { 967 LOG.error(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_PARSE_ERROR_1, e.toString()), e); 968 } 969 m_parseError = true; 970 } 971 if (m_parseError) { 972 // If string is invalid set cache to "never" 973 m_always = -1; 974 } 975 } 976 977 /** 978 * A helper method for the parsing process which parses 979 * Strings like groups=(a, b, c).<p> 980 * 981 * @param value the String to parse 982 * @return a Map that contains of the parsed values, only the keyset of the Map is needed later 983 */ 984 private Set<String> parseValueList(String value) { 985 986 if (value.charAt(0) == '(') { 987 value = value.substring(1); 988 } 989 int len = value.length() - 1; 990 if (value.charAt(len) == ')') { 991 value = value.substring(0, len); 992 } 993 if (value.charAt(len - 1) == ',') { 994 value = value.substring(0, len - 1); 995 } 996 if (LOG.isDebugEnabled()) { 997 LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXCACHEKEY_PARSE_VALUES_1, value)); 998 } 999 List<String> tokens = CmsStringUtil.splitAsList(value, ',', true); 1000 Set<String> result = new HashSet<String>(); 1001 result.addAll(tokens); 1002 return result; 1003 } 1004}