1 5 package com.opensymphony.oscache.web.tag; 6 7 import com.opensymphony.oscache.base.Cache; 8 import com.opensymphony.oscache.base.NeedsRefreshException; 9 import com.opensymphony.oscache.util.StringUtil; 10 import com.opensymphony.oscache.web.ServletCacheAdministrator; 11 import com.opensymphony.oscache.web.WebEntryRefreshPolicy; 12 13 import org.apache.commons.logging.Log; 14 import org.apache.commons.logging.LogFactory; 15 16 import java.io.IOException ; 17 18 import java.util.ArrayList ; 19 import java.util.List ; 20 21 import javax.servlet.http.HttpServletRequest ; 22 import javax.servlet.jsp.JspTagException ; 23 import javax.servlet.jsp.PageContext ; 24 import javax.servlet.jsp.tagext.BodyTagSupport ; 25 import javax.servlet.jsp.tagext.TryCatchFinally ; 26 27 49 public class CacheTag extends BodyTagSupport implements TryCatchFinally { 50 53 private final static int SECOND = 1; 54 private final static int MINUTE = 60 * SECOND; 55 private final static int HOUR = 60 * MINUTE; 56 private final static int DAY = 24 * HOUR; 57 private final static int WEEK = 7 * DAY; 58 private final static int MONTH = 30 * DAY; 59 private final static int YEAR = 365 * DAY; 60 61 64 private final static String CACHE_TAG_COUNTER_KEY = "__oscache_tag_counter"; 65 66 69 final static private int ONE_MINUTE = 60; 70 final static private int ONE_HOUR = 60 * ONE_MINUTE; 71 final static private int DEFAULT_TIMEOUT = ONE_HOUR; 72 private static transient Log log = LogFactory.getLog(CacheTag.class); 73 74 77 final static private int SILENT_MODE = 1; 78 79 83 boolean cancelUpdateRequired = false; 84 private Cache cache = null; 85 86 89 private List groups = null; 90 private ServletCacheAdministrator admin = null; 91 92 95 private String actualKey = null; 96 97 100 private String content = null; 101 102 105 private String cron = null; 106 107 110 private String key = null; 111 112 115 private String language = null; 116 117 120 private String refreshPolicyClass = null; 121 122 126 private String refreshPolicyParam = null; 127 128 131 private boolean refresh = false; 132 133 136 private boolean useBody = true; 137 138 141 private int mode = 0; 142 143 146 private int scope = PageContext.APPLICATION_SCOPE; 147 148 151 private int time = DEFAULT_TIMEOUT; 152 153 173 public void setDuration(String duration) { 174 try { 175 this.time = parseDuration(duration); 177 } catch (Exception ex) { 178 if (log.isDebugEnabled()) { 179 log.debug("Failed parsing simple duration format '" + duration + "' (" + ex.getMessage() + "). Trying ISO-8601 format..."); 180 } 181 182 try { 183 this.time = parseISO_8601_Duration(duration); 185 } catch (Exception ex1) { 186 log.warn("The requested cache duration '" + duration + "' is invalid (" + ex1.getMessage() + "). Reverting to the default timeout"); 189 this.time = DEFAULT_TIMEOUT; 190 } 191 } 192 } 193 194 198 public void setCron(String cron) { 199 this.cron = cron; 200 } 201 202 208 public void setGroups(String groups) { 209 this.groups = StringUtil.split(groups, ','); 211 } 212 213 218 void addGroup(String group) { 219 if (groups == null) { 220 groups = new ArrayList (); 222 } 223 224 groups.add(group); 225 } 226 227 232 void addGroups(String groupsString) { 233 if (groups == null) { 234 groups = new ArrayList (); 236 } 237 238 groups.addAll(StringUtil.split(groupsString, ',')); 239 } 240 241 246 public void setKey(String key) { 247 this.key = key; 248 } 249 250 255 public void setLanguage(String language) { 256 this.language = language; 257 } 258 259 265 public void setRefresh(boolean refresh) { 266 this.refresh = refresh; 267 } 268 269 274 public void setMode(String mode) { 275 if ("silent".equalsIgnoreCase(mode)) { 276 this.mode = SILENT_MODE; 277 } else { 278 this.mode = 0; 279 } 280 } 281 282 285 public void setRefreshpolicyclass(String refreshPolicyClass) { 286 this.refreshPolicyClass = refreshPolicyClass; 287 } 288 289 293 public void setRefreshpolicyparam(String refreshPolicyParam) { 294 this.refreshPolicyParam = refreshPolicyParam; 295 } 296 297 299 304 public void setScope(String scope) { 305 if (scope.equalsIgnoreCase(ServletCacheAdministrator.SESSION_SCOPE_NAME)) { 306 this.scope = PageContext.SESSION_SCOPE; 307 } else { 308 this.scope = PageContext.APPLICATION_SCOPE; 309 } 310 } 311 312 320 public void setTime(int time) { 321 this.time = time; 322 } 323 324 333 public void setUseBody(boolean useBody) { 334 if (log.isDebugEnabled()) { 335 log.debug("<cache>: Set useBody to " + useBody); 336 } 337 338 this.useBody = useBody; 339 } 340 341 348 public int doAfterBody() throws JspTagException { 349 String body = null; 350 351 try { 352 if ((bodyContent != null) && (useBody || (time == 0)) && ((body = bodyContent.getString()) != null)) { 354 if ((time != 0) || (refreshPolicyClass != null)) { 355 WebEntryRefreshPolicy policy = null; 357 358 if (refreshPolicyClass != null) { 359 try { 360 policy = (WebEntryRefreshPolicy) Class.forName(refreshPolicyClass).newInstance(); 361 policy.init(actualKey, refreshPolicyParam); 362 } catch (Exception e) { 363 if (log.isInfoEnabled()) { 364 log.info("<cache>: Problem instantiating or initializing refresh policy : " + refreshPolicyClass); 365 } 366 } 367 } 368 369 if (log.isDebugEnabled()) { 370 log.debug("<cache>: Updating cache entry with new content : " + actualKey); 371 } 372 373 cancelUpdateRequired = false; 374 375 if ((groups == null) || groups.isEmpty()) { 376 cache.putInCache(actualKey, body, policy); 377 } else { 378 String [] groupArray = new String [groups.size()]; 379 groups.toArray(groupArray); 380 cache.putInCache(actualKey, body, groupArray, policy, null); 381 } 382 } 383 } 384 else { 386 if (!useBody && (content != null)) { 387 if (log.isInfoEnabled()) { 388 log.info("<cache>: Using cached version as instructed, useBody = false : " + actualKey); 389 } 390 391 body = content; 392 } 393 else { 395 if (log.isInfoEnabled()) { 396 log.info("<cache>: Missing cached content : " + actualKey); 397 } 398 399 body = "Missing cached content"; 400 } 401 } 402 403 if (mode != SILENT_MODE) { 405 bodyContent.clearBody(); 406 bodyContent.write(body); 407 bodyContent.writeOut(bodyContent.getEnclosingWriter()); 408 } 409 } catch (java.io.IOException e) { 410 throw new JspTagException ("IO Error: " + e.getMessage()); 411 } 412 413 return SKIP_BODY; 414 } 415 416 public void doCatch(Throwable throwable) throws Throwable { 417 throw throwable; 418 } 419 420 426 public int doEndTag() throws JspTagException { 427 return EVAL_PAGE; 428 } 429 430 public void doFinally() { 431 if (cancelUpdateRequired && (actualKey != null)) { 432 cache.cancelUpdate(actualKey); 433 } 434 435 groups = null; 437 scope = PageContext.APPLICATION_SCOPE; 438 cron = null; 439 key = null; 440 language = null; 441 refreshPolicyClass = null; 442 refreshPolicyParam = null; 443 time = DEFAULT_TIMEOUT; 444 refresh = false; 445 mode = 0; 446 } 447 448 459 public int doStartTag() throws JspTagException { 460 cancelUpdateRequired = false; 461 useBody = true; 462 content = null; 463 464 int returnCode = EVAL_BODY_BUFFERED; 466 467 if (admin == null) { 468 admin = ServletCacheAdministrator.getInstance(pageContext.getServletContext()); 469 } 470 471 if (scope == PageContext.SESSION_SCOPE) { 473 cache = admin.getSessionScopeCache(((HttpServletRequest ) pageContext.getRequest()).getSession(true)); 474 } else { 475 cache = admin.getAppScopeCache(pageContext.getServletContext()); 476 } 477 478 String suffix = null; 482 483 if (key == null) { 484 synchronized (pageContext.getRequest()) { 485 Object o = pageContext.getRequest().getAttribute(CACHE_TAG_COUNTER_KEY); 486 487 if (o == null) { 488 suffix = "1"; 489 } else { 490 suffix = Integer.toString(Integer.parseInt((String ) o) + 1); 491 } 492 } 493 494 pageContext.getRequest().setAttribute(CACHE_TAG_COUNTER_KEY, suffix); 495 } 496 497 actualKey = admin.generateEntryKey(key, (HttpServletRequest ) pageContext.getRequest(), scope, language, suffix); 499 500 508 try { 509 if (refresh) { 510 content = (String ) cache.getFromCache(actualKey, 0, cron); 512 } else { 513 content = (String ) cache.getFromCache(actualKey, time, cron); 515 } 516 517 try { 518 if (log.isDebugEnabled()) { 519 log.debug("<cache>: Using cached entry : " + actualKey); 520 } 521 522 if ((content != null)) { 524 if (mode != SILENT_MODE) { 525 pageContext.getOut().write(content); 526 } 527 528 returnCode = SKIP_BODY; 529 } 530 } catch (IOException e) { 531 throw new JspTagException ("IO Exception: " + e.getMessage()); 532 } 533 } catch (NeedsRefreshException nre) { 534 cancelUpdateRequired = true; 535 content = (String ) nre.getCacheContent(); 536 } 537 538 if (returnCode == EVAL_BODY_BUFFERED) { 539 if (log.isDebugEnabled()) { 540 log.debug("<cache>: Cached content not used: New cache entry, cache stale or scope flushed : " + actualKey); 541 } 542 } 543 544 return returnCode; 545 } 546 547 560 private int parseDuration(String duration) { 561 int time = 0; 562 563 try { 565 time = Integer.parseInt(duration); 566 } catch (Exception ex) { 567 for (int i = 0; i < duration.length(); i++) { 569 if (!Character.isDigit(duration.charAt(i))) { 570 time = Integer.parseInt(duration.substring(0, i)); 571 572 switch ((int) duration.charAt(i)) { 573 case (int) 's': 574 time *= SECOND; 575 break; 576 case (int) 'm': 577 time *= MINUTE; 578 break; 579 case (int) 'h': 580 time *= HOUR; 581 break; 582 case (int) 'd': 583 time *= DAY; 584 break; 585 case (int) 'w': 586 time *= WEEK; 587 break; 588 default: 589 } 591 592 break; 593 } 594 595 } 597 598 } 600 601 return time; 603 } 604 605 612 private int parseISO_8601_Duration(String duration) throws Exception { 613 int years = 0; 614 int months = 0; 615 int days = 0; 616 int hours = 0; 617 int mins = 0; 618 int secs = 0; 619 620 int index = duration.indexOf("-"); 623 624 if (index > 0) { 625 throw new Exception ("Invalid duration (- must be at the beginning)"); 626 } 627 628 String workValue = duration.substring(index + 1); 630 631 if (workValue.charAt(0) != 'P') { 632 throw new Exception ("Invalid duration (P must be at the beginning)"); 633 } 634 635 workValue = workValue.substring(1); 637 638 if (workValue.length() == 0) { 639 throw new Exception ("Invalid duration (nothing specified)"); 640 } 641 642 index = workValue.indexOf('T'); 644 645 String timeString = ""; 646 647 if (index > 0) { 648 timeString = workValue.substring(index + 1); 649 650 if (timeString.equals("")) { 652 throw new Exception ("Invalid duration (T with no time)"); 653 } 654 655 workValue = workValue.substring(0, index); 656 } else if (index == 0) { 657 timeString = workValue.substring(1); 658 workValue = ""; 659 } 660 661 if (!workValue.equals("")) { 662 validateDateFormat(workValue); 663 664 int yearIndex = workValue.indexOf('Y'); 665 int monthIndex = workValue.indexOf('M'); 666 int dayIndex = workValue.indexOf('D'); 667 668 if ((yearIndex != -1) && (monthIndex != -1) && (yearIndex > monthIndex)) { 669 throw new Exception ("Invalid duration (Date part not properly specified)"); 670 } 671 672 if ((yearIndex != -1) && (dayIndex != -1) && (yearIndex > dayIndex)) { 673 throw new Exception ("Invalid duration (Date part not properly specified)"); 674 } 675 676 if ((dayIndex != -1) && (monthIndex != -1) && (monthIndex > dayIndex)) { 677 throw new Exception ("Invalid duration (Date part not properly specified)"); 678 } 679 680 if (yearIndex >= 0) { 681 years = (new Integer (workValue.substring(0, yearIndex))).intValue(); 682 } 683 684 if (monthIndex >= 0) { 685 months = (new Integer (workValue.substring(yearIndex + 1, monthIndex))).intValue(); 686 } 687 688 if (dayIndex >= 0) { 689 if (monthIndex >= 0) { 690 days = (new Integer (workValue.substring(monthIndex + 1, dayIndex))).intValue(); 691 } else { 692 if (yearIndex >= 0) { 693 days = (new Integer (workValue.substring(yearIndex + 1, dayIndex))).intValue(); 694 } else { 695 days = (new Integer (workValue.substring(0, dayIndex))).intValue(); 696 } 697 } 698 } 699 } 700 701 if (!timeString.equals("")) { 702 validateHourFormat(timeString); 703 704 int hourIndex = timeString.indexOf('H'); 705 int minuteIndex = timeString.indexOf('M'); 706 int secondIndex = timeString.indexOf('S'); 707 708 if ((hourIndex != -1) && (minuteIndex != -1) && (hourIndex > minuteIndex)) { 709 throw new Exception ("Invalid duration (Time part not properly specified)"); 710 } 711 712 if ((hourIndex != -1) && (secondIndex != -1) && (hourIndex > secondIndex)) { 713 throw new Exception ("Invalid duration (Time part not properly specified)"); 714 } 715 716 if ((secondIndex != -1) && (minuteIndex != -1) && (minuteIndex > secondIndex)) { 717 throw new Exception ("Invalid duration (Time part not properly specified)"); 718 } 719 720 if (hourIndex >= 0) { 721 hours = (new Integer (timeString.substring(0, hourIndex))).intValue(); 722 } 723 724 if (minuteIndex >= 0) { 725 mins = (new Integer (timeString.substring(hourIndex + 1, minuteIndex))).intValue(); 726 } 727 728 if (secondIndex >= 0) { 729 if (timeString.length() != (secondIndex + 1)) { 730 throw new Exception ("Invalid duration (Time part not properly specified)"); 731 } 732 733 if (minuteIndex >= 0) { 734 timeString = timeString.substring(minuteIndex + 1, timeString.length() - 1); 735 } else { 736 if (hourIndex >= 0) { 737 timeString = timeString.substring(hourIndex + 1, timeString.length() - 1); 738 } else { 739 timeString = timeString.substring(0, timeString.length() - 1); 740 } 741 } 742 743 if (timeString.indexOf('.') == (timeString.length() - 1)) { 744 throw new Exception ("Invalid duration (Time part not properly specified)"); 745 } 746 747 secs = (new Double (timeString)).intValue(); 748 } 749 } 750 751 return secs + (mins * MINUTE) + (hours * HOUR) + (days * DAY) + (months * MONTH) + (years * YEAR); 753 } 754 755 761 private void validateDateFormat(String basicDate) throws Exception { 762 int yearCounter = 0; 763 int monthCounter = 0; 764 int dayCounter = 0; 765 766 for (int counter = 0; counter < basicDate.length(); counter++) { 767 if (!Character.isDigit(basicDate.charAt(counter)) && (basicDate.charAt(counter) != 'Y') && (basicDate.charAt(counter) != 'M') && (basicDate.charAt(counter) != 'D')) { 769 throw new Exception ("Invalid duration (Date part not properly specified)"); 770 } 771 772 if (basicDate.charAt(counter) == 'Y') { 774 yearCounter++; 775 } 776 777 if (basicDate.charAt(counter) == 'M') { 778 monthCounter++; 779 } 780 781 if (basicDate.charAt(counter) == 'D') { 782 dayCounter++; 783 } 784 } 785 786 if ((yearCounter > 1) || (monthCounter > 1) || (dayCounter > 1)) { 787 throw new Exception ("Invalid duration (Date part not properly specified)"); 788 } 789 } 790 791 797 private void validateHourFormat(String basicHour) throws Exception { 798 int minuteCounter = 0; 799 int secondCounter = 0; 800 int hourCounter = 0; 801 802 for (int counter = 0; counter < basicHour.length(); counter++) { 803 if (!Character.isDigit(basicHour.charAt(counter)) && (basicHour.charAt(counter) != 'H') && (basicHour.charAt(counter) != 'M') && (basicHour.charAt(counter) != 'S') && (basicHour.charAt(counter) != '.')) { 804 throw new Exception ("Invalid duration (Time part not properly specified)"); 805 } 806 807 if (basicHour.charAt(counter) == 'H') { 808 hourCounter++; 809 } 810 811 if (basicHour.charAt(counter) == 'M') { 812 minuteCounter++; 813 } 814 815 if (basicHour.charAt(counter) == 'S') { 816 secondCounter++; 817 } 818 } 819 820 if ((hourCounter > 1) || (minuteCounter > 1) || (secondCounter > 1)) { 821 throw new Exception ("Invalid duration (Time part not properly specified)"); 822 } 823 } 824 } 825 | Popular Tags |