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