1 5 package com.opensymphony.oscache.web; 6 7 import com.opensymphony.oscache.base.*; 8 import com.opensymphony.oscache.base.events.CacheEventListener; 9 import com.opensymphony.oscache.base.events.ScopeEvent; 10 import com.opensymphony.oscache.base.events.ScopeEventListener; 11 import com.opensymphony.oscache.base.events.ScopeEventType; 12 13 import org.apache.commons.logging.Log; 14 import org.apache.commons.logging.LogFactory; 15 16 import java.io.Serializable ; 17 18 import java.util.*; 19 20 import javax.servlet.ServletContext ; 21 import javax.servlet.http.HttpServletRequest ; 22 import javax.servlet.http.HttpSession ; 23 import javax.servlet.jsp.PageContext ; 24 25 41 public class ServletCacheAdministrator extends AbstractCacheAdministrator implements Serializable { 42 private static final transient Log log = LogFactory.getLog(ServletCacheAdministrator.class); 43 44 47 private final static String CACHE_USE_HOST_DOMAIN_KEY = "cache.use.host.domain.in.key"; 48 private final static String CACHE_KEY_KEY = "cache.key"; 49 50 53 private final static String DEFAULT_CACHE_KEY = "__oscache_cache"; 54 55 58 public final static String SESSION_SCOPE_NAME = "session"; 59 public final static String APPLICATION_SCOPE_NAME = "application"; 60 61 64 private final static String CACHE_ADMINISTRATOR_KEY = "__oscache_admin"; 65 66 71 public final static String HASH_KEY_SCOPE = "scope"; 72 73 78 public final static String HASH_KEY_SESSION_ID = "sessionId"; 79 80 85 public final static String HASH_KEY_CONTEXT_TMPDIR = "context.tempdir"; 86 87 90 private final static String FILE_SEPARATOR = "/"; 91 92 95 private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0); 96 97 100 private final static short AVERAGE_KEY_LENGTH = 30; 101 102 105 private static final String m_strBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 106 107 110 private Map flushTimes; 111 112 115 private transient ServletContext context; 116 117 120 private String cacheKey; 121 122 126 private boolean useHostDomainInKey = false; 127 128 133 private ServletCacheAdministrator(ServletContext context, Properties p) { 134 super(p); 135 config.set(HASH_KEY_CONTEXT_TMPDIR, context.getAttribute("javax.servlet.context.tempdir")); 136 137 flushTimes = new HashMap(); 138 initHostDomainInKey(); 139 this.context = context; 140 } 141 142 148 public static ServletCacheAdministrator getInstance(ServletContext context) { 149 return getInstance(context, null); 150 } 151 152 162 public synchronized static ServletCacheAdministrator getInstance(ServletContext context, Properties p) { 163 164 ServletCacheAdministrator admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY); 165 166 if (admin == null) { 169 admin = new ServletCacheAdministrator(context, p); 170 context.setAttribute(CACHE_ADMINISTRATOR_KEY, admin); 171 172 if (log.isInfoEnabled()) { 173 log.info("Created new instance of ServletCacheAdministrator"); 174 } 175 176 admin.getAppScopeCache(context); 177 } 178 179 if (admin.context == null) { 180 admin.context = context; 181 } 182 183 return admin; 184 } 185 186 190 public static void destroyInstance(ServletContext context) { 191 ServletCacheAdministrator admin; 192 admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY); 193 194 if (admin != null) { 195 Cache cache = (Cache) context.getAttribute(admin.getCacheKey()); 197 198 if (cache != null) { 199 admin.finalizeListeners(cache); 200 context.removeAttribute(admin.getCacheKey()); 201 context.removeAttribute(CACHE_ADMINISTRATOR_KEY); 202 cache = null; 203 204 if (log.isInfoEnabled()) { 205 log.info("Shut down the ServletCacheAdministrator"); 206 } 207 } 208 209 admin = null; 210 } 211 } 212 213 221 public Cache getCache(HttpServletRequest request, int scope) { 222 if (scope == PageContext.APPLICATION_SCOPE) { 223 return getAppScopeCache(context); 224 } 225 226 if (scope == PageContext.SESSION_SCOPE) { 227 return getSessionScopeCache(request.getSession(true)); 228 } 229 230 throw new RuntimeException ("The supplied scope value of " + scope + " is invalid. Acceptable values are PageContext.APPLICATION_SCOPE and PageContext.SESSION_SCOPE"); 231 } 232 233 240 public Cache getAppScopeCache(ServletContext context) { 241 Cache cache; 242 Object obj = context.getAttribute(getCacheKey()); 243 244 if ((obj == null) || !(obj instanceof Cache)) { 245 if (log.isInfoEnabled()) { 246 log.info("Created new application-scoped cache at key: " + getCacheKey()); 247 } 248 249 cache = createCache(PageContext.APPLICATION_SCOPE, null); 250 context.setAttribute(getCacheKey(), cache); 251 } else { 252 cache = (Cache) obj; 253 } 254 255 return cache; 256 } 257 258 265 public Cache getSessionScopeCache(HttpSession session) { 266 Cache cache; 267 Object obj = session.getAttribute(getCacheKey()); 268 269 if ((obj == null) || !(obj instanceof Cache)) { 270 if (log.isInfoEnabled()) { 271 log.info("Created new session-scoped cache in session " + session.getId() + " at key: " + getCacheKey()); 272 } 273 274 cache = createCache(PageContext.SESSION_SCOPE, session.getId()); 275 session.setAttribute(getCacheKey(), cache); 276 } else { 277 cache = (Cache) obj; 278 } 279 280 return cache; 281 } 282 283 289 public String getCacheKey() { 290 if (cacheKey == null) { 291 cacheKey = getProperty(CACHE_KEY_KEY); 292 293 if (cacheKey == null) { 294 cacheKey = DEFAULT_CACHE_KEY; 295 } 296 } 297 298 return cacheKey; 299 } 300 301 307 public void setFlushTime(Date date, int scope) { 308 if (log.isInfoEnabled()) { 309 log.info("Flushing scope " + scope + " at " + date); 310 } 311 312 synchronized (flushTimes) { 313 if (date != null) { 314 dispatchScopeEvent(ScopeEventType.SCOPE_FLUSHED, scope, date, null); 316 flushTimes.put(new Integer (scope), date); 317 } else { 318 logError("setFlushTime called with a null date."); 319 throw new IllegalArgumentException ("setFlushTime called with a null date."); 320 } 321 } 322 } 323 324 329 public void setFlushTime(int scope) { 330 setFlushTime(new Date(), scope); 331 } 332 333 340 public Date getFlushTime(int scope) { 341 synchronized (flushTimes) { 342 return (Date) flushTimes.get(new Integer (scope)); 343 } 344 } 345 346 356 public Object getFromCache(int scope, HttpServletRequest request, String key, int refreshPeriod) throws NeedsRefreshException { 357 Cache cache = getCache(request, scope); 358 key = this.generateEntryKey(key, request, scope); 359 return cache.getFromCache(key, refreshPeriod); 360 } 361 362 371 public boolean isScopeFlushed(CacheEntry cacheEntry, int scope) { 372 Date flushDateTime = getFlushTime(scope); 373 374 if (flushDateTime != null) { 375 long lastUpdate = cacheEntry.getLastUpdate(); 376 return (flushDateTime.getTime() >= lastUpdate); 377 } else { 378 return false; 379 } 380 } 381 382 387 public void addScopeEventListener(ScopeEventListener listener) { 388 listenerList.add(ScopeEventListener.class, listener); 389 } 390 391 400 public void cancelUpdate(int scope, HttpServletRequest request, String key) { 401 Cache cache = getCache(request, scope); 402 key = this.generateEntryKey(key, request, scope); 403 cache.cancelUpdate(key); 404 } 405 406 411 public void flushAll(Date date) { 412 synchronized (flushTimes) { 413 setFlushTime(date, PageContext.APPLICATION_SCOPE); 414 setFlushTime(date, PageContext.SESSION_SCOPE); 415 setFlushTime(date, PageContext.REQUEST_SCOPE); 416 setFlushTime(date, PageContext.PAGE_SCOPE); 417 } 418 419 dispatchScopeEvent(ScopeEventType.ALL_SCOPES_FLUSHED, -1, date, null); 421 } 422 423 426 public void flushAll() { 427 flushAll(new Date()); 428 } 429 430 447 public String generateEntryKey(String key, HttpServletRequest request, int scope) { 448 return generateEntryKey(key, request, scope, null, null); 449 } 450 451 469 public String generateEntryKey(String key, HttpServletRequest request, int scope, String language) { 470 return generateEntryKey(key, request, scope, language, null); 471 } 472 473 492 public String generateEntryKey(String key, HttpServletRequest request, int scope, String language, String suffix) { 493 496 StringBuffer cBuffer = new StringBuffer (AVERAGE_KEY_LENGTH); 497 498 if (language != null) { 500 cBuffer.append(FILE_SEPARATOR).append(language); 501 } 502 503 if (useHostDomainInKey) { 505 cBuffer.append(FILE_SEPARATOR).append(request.getServerName()); 506 } 507 508 if (key != null) { 509 cBuffer.append(FILE_SEPARATOR).append(key); 510 } else { 511 String generatedKey = request.getRequestURI(); 512 513 if (generatedKey.charAt(0) != FILE_SEPARATOR_CHAR) { 514 cBuffer.append(FILE_SEPARATOR_CHAR); 515 } 516 517 cBuffer.append(generatedKey); 518 cBuffer.append("_").append(request.getMethod()).append("_"); 519 520 generatedKey = getSortedQueryString(request); 521 522 if (generatedKey != null) { 523 try { 524 java.security.MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); 525 byte[] b = digest.digest(generatedKey.getBytes()); 526 cBuffer.append("_"); 527 528 cBuffer.append(toBase64(b).replace('/', '_')); 530 } catch (Exception e) { 531 } 533 } 534 } 535 536 if ((suffix != null) && (suffix.length() > 0)) { 538 cBuffer.append(suffix); 539 } 540 541 return cBuffer.toString(); 542 } 543 544 552 protected String getSortedQueryString(HttpServletRequest request) { 553 Map paramMap = request.getParameterMap(); 554 555 if (paramMap.isEmpty()) { 556 return null; 557 } 558 559 Set paramSet = new TreeMap(paramMap).entrySet(); 560 561 StringBuffer buf = new StringBuffer (); 562 563 boolean first = true; 564 565 for (Iterator it = paramSet.iterator(); it.hasNext();) { 566 Map.Entry entry = (Map.Entry) it.next(); 567 String [] values = (String []) entry.getValue(); 568 569 for (int i = 0; i < values.length; i++) { 570 String key = (String ) entry.getKey(); 571 572 if ((key.length() != 10) || !"jsessionid".equals(key)) { 573 if (first) { 574 first = false; 575 } else { 576 buf.append('&'); 577 } 578 579 buf.append(key).append('=').append(values[i]); 580 } 581 } 582 } 583 584 if (buf.length() == 0) { 586 return null; 587 } else { 588 return buf.toString(); 589 } 590 } 591 592 597 public void logError(String message) { 598 log.error("[oscache]: " + message); 599 } 600 601 609 public void putInCache(int scope, HttpServletRequest request, String key, Object content) { 610 putInCache(scope, request, key, content, null); 611 } 612 613 622 public void putInCache(int scope, HttpServletRequest request, String key, Object content, EntryRefreshPolicy policy) { 623 Cache cache = getCache(request, scope); 624 key = this.generateEntryKey(key, request, scope); 625 cache.putInCache(key, content, policy); 626 } 627 628 637 public void setCacheCapacity(int scope, HttpServletRequest request, int capacity) { 638 setCacheCapacity(capacity); 639 getCache(request, scope).setCapacity(capacity); 640 } 641 642 647 public void removeScopeEventListener(ScopeEventListener listener) { 648 listenerList.remove(ScopeEventListener.class, listener); 649 } 650 651 654 protected void finalizeListeners(Cache cache) { 655 super.finalizeListeners(cache); 656 } 657 658 661 private static String toBase64(byte[] aValue) { 662 int byte1; 663 int byte2; 664 int byte3; 665 int iByteLen = aValue.length; 666 StringBuffer tt = new StringBuffer (); 667 668 for (int i = 0; i < iByteLen; i += 3) { 669 boolean bByte2 = (i + 1) < iByteLen; 670 boolean bByte3 = (i + 2) < iByteLen; 671 byte1 = aValue[i] & 0xFF; 672 byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0; 673 byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0; 674 675 tt.append(m_strBase64Chars.charAt(byte1 / 4)); 676 tt.append(m_strBase64Chars.charAt((byte2 / 16) + ((byte1 & 0x3) * 16))); 677 tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64) + ((byte2 & 0xF) * 4)) : '=')); 678 tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : '=')); 679 } 680 681 return tt.toString(); 682 } 683 684 691 private ServletCache createCache(int scope, String sessionId) { 692 ServletCache newCache = new ServletCache(this, algorithmClass, cacheCapacity, scope); 693 694 config.set(HASH_KEY_SCOPE, "" + scope); 699 config.set(HASH_KEY_SESSION_ID, sessionId); 700 701 newCache = (ServletCache) configureStandardListeners(newCache); 702 703 if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) { 704 CacheEventListener[] listeners = getCacheEventListeners(); 706 707 for (int i = 0; i < listeners.length; i++) { 708 if (listeners[i] instanceof ScopeEventListener) { 709 newCache.addCacheEventListener(listeners[i], ScopeEventListener.class); 710 } 711 } 712 } 713 714 return newCache; 715 } 716 717 725 private void dispatchScopeEvent(ScopeEventType eventType, int scope, Date date, String origin) { 726 ScopeEvent event = new ScopeEvent(eventType, scope, date, origin); 728 729 Object [] listeners = listenerList.getListenerList(); 731 732 for (int i = listeners.length - 2; i >= 0; i -= 2) { 735 if (listeners[i] == ScopeEventListener.class) { 736 ((ScopeEventListener) listeners[i + 1]).scopeFlushed(event); 737 } 738 } 739 } 740 741 745 private void initHostDomainInKey() { 746 String propStr = getProperty(CACHE_USE_HOST_DOMAIN_KEY); 747 748 useHostDomainInKey = "true".equalsIgnoreCase(propStr); 749 } 750 } 751 | Popular Tags |