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