Clover coverage report -
Coverage timestamp: So Nov 6 2005 14:19:51 CET
file stats: LOC: 750   Methods: 31
NCLOC: 314   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ServletCacheAdministrator.java 0% 0% 0% 0%
coverage
 1    /*
 2    * Copyright (c) 2002-2003 by OpenSymphony
 3    * All rights reserved.
 4    */
 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    /**
 26    * A ServletCacheAdministrator creates, flushes and administers the cache.
 27    * <p>
 28    * This is a "servlet Singleton". This means it's not a Singleton in the traditional sense,
 29    * that is stored in a static instance. It's a Singleton _per web app context_.
 30    * <p>
 31    * Once created it manages the cache path on disk through the oscache.properties
 32    * file, and also keeps track of the flush times.
 33    *
 34    * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
 35    * @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a>
 36    * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
 37    * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
 38    * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
 39    * @version $Revision: 1.2 $
 40    */
 41    public class ServletCacheAdministrator extends AbstractCacheAdministrator implements Serializable {
 42    private static final transient Log log = LogFactory.getLog(ServletCacheAdministrator.class);
 43   
 44    /**
 45    * Constants for properties read/written from/to file
 46    */
 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    /**
 51    * The default cache key that is used to store the cache in context.
 52    */
 53    private final static String DEFAULT_CACHE_KEY = "__oscache_cache";
 54   
 55    /**
 56    * Constants for scope's name
 57    */
 58    public final static String SESSION_SCOPE_NAME = "session";
 59    public final static String APPLICATION_SCOPE_NAME = "application";
 60   
 61    /**
 62    * The key under which the CacheAdministrator will be stored in the ServletContext
 63    */
 64    private final static String CACHE_ADMINISTRATOR_KEY = "__oscache_admin";
 65   
 66    /**
 67    * Key used to store the current scope in the configuration. This is a hack
 68    * to let the scope information get passed through to the DiskPersistenceListener,
 69    * and will be removed in a future release.
 70    */
 71    public final static String HASH_KEY_SCOPE = "scope";
 72   
 73    /**
 74    * Key used to store the current session ID in the configuration. This is a hack
 75    * to let the scope information get passed through to the DiskPersistenceListener,
 76    * and will be removed in a future release.
 77    */
 78    public final static String HASH_KEY_SESSION_ID = "sessionId";
 79   
 80    /**
 81    * Key used to store the servlet container temporary directory in the configuration.
 82    * This is a hack to let the scope information get passed through to the
 83    * DiskPersistenceListener, and will be removed in a future release.
 84    */
 85    public final static String HASH_KEY_CONTEXT_TMPDIR = "context.tempdir";
 86   
 87    /**
 88    * The string to use as a file separator.
 89    */
 90    private final static String FILE_SEPARATOR = "/";
 91   
 92    /**
 93    * The character to use as a file separator.
 94    */
 95    private final static char FILE_SEPARATOR_CHAR = FILE_SEPARATOR.charAt(0);
 96   
 97    /**
 98    * Constant for Key generation.
 99    */
 100    private final static short AVERAGE_KEY_LENGTH = 30;
 101   
 102    /**
 103    * Usable caracters for key generation
 104    */
 105    private static final String m_strBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 106   
 107    /**
 108    * Map containing the flush times of different scopes
 109    */
 110    private Map flushTimes;
 111   
 112    /**
 113    * Required so we can look up the app scope cache without forcing a session creation.
 114    */
 115    private transient ServletContext context;
 116   
 117    /**
 118    * Key to use for storing and retrieving Object in contexts (Servlet, session).
 119    */
 120    private String cacheKey;
 121   
 122    /**
 123    * Set property cache.use.host.domain.in.key=true to add domain information to key
 124    * generation for hosting multiple sites.
 125    */
 126    private boolean useHostDomainInKey = false;
 127   
 128    /**
 129    * Create the cache administrator.
 130    *
 131    * This will reset all the flush times and load the properties file.
 132    */
 133  0 private ServletCacheAdministrator(ServletContext context, Properties p) {
 134  0 super(p);
 135  0 config.set(HASH_KEY_CONTEXT_TMPDIR, context.getAttribute("javax.servlet.context.tempdir"));
 136   
 137  0 flushTimes = new HashMap();
 138  0 initHostDomainInKey();
 139  0 this.context = context;
 140    }
 141   
 142    /**
 143    * Obtain an instance of the CacheAdministrator
 144    *
 145    * @param context The ServletContext that this CacheAdministrator is a Singleton under
 146    * @return Returns the CacheAdministrator instance for this context
 147    */
 148  0 public static ServletCacheAdministrator getInstance(ServletContext context) {
 149  0 return getInstance(context, null);
 150    }
 151   
 152    /**
 153    * Obtain an instance of the CacheAdministrator
 154    *
 155    * @param context The ServletContext that this CacheAdministrator is a Singleton under
 156    * @param p the properties to use for the cache if the cache administrator has not been
 157    * created yet. Once the administrator has been created, the properties parameter is
 158    * ignored for all future invocations. If a null value is passed in, then the properties
 159    * are loaded from the oscache.properties file in the classpath.
 160    * @return Returns the CacheAdministrator instance for this context
 161    */
 162  0 public synchronized static ServletCacheAdministrator getInstance(ServletContext context, Properties p) {
 163   
 164  0 ServletCacheAdministrator admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
 165   
 166    // First time we need to create the administrator and store it in the
 167    // servlet context
 168  0 if (admin == null) {
 169  0 admin = new ServletCacheAdministrator(context, p);
 170  0 context.setAttribute(CACHE_ADMINISTRATOR_KEY, admin);
 171   
 172  0 if (log.isInfoEnabled()) {
 173  0 log.info("Created new instance of ServletCacheAdministrator");
 174    }
 175   
 176  0 admin.getAppScopeCache(context);
 177    }
 178   
 179  0 if (admin.context == null) {
 180  0 admin.context = context;
 181    }
 182   
 183  0 return admin;
 184    }
 185   
 186    /**
 187    * Shuts down the cache administrator. This should usually only be called
 188    * when the controlling application shuts down.
 189    */
 190  0 public static void destroyInstance(ServletContext context) {
 191  0 ServletCacheAdministrator admin;
 192  0 admin = (ServletCacheAdministrator) context.getAttribute(CACHE_ADMINISTRATOR_KEY);
 193   
 194  0 if (admin != null) {
 195    // Finalize the application scope cache
 196  0 Cache cache = (Cache) context.getAttribute(admin.getCacheKey());
 197   
 198  0 if (cache != null) {
 199  0 admin.finalizeListeners(cache);
 200  0 context.removeAttribute(admin.getCacheKey());
 201  0 context.removeAttribute(CACHE_ADMINISTRATOR_KEY);
 202  0 cache = null;
 203   
 204  0 if (log.isInfoEnabled()) {
 205  0 log.info("Shut down the ServletCacheAdministrator");
 206    }
 207    }
 208   
 209  0 admin = null;
 210    }
 211    }
 212   
 213    /**
 214    * Grabs the cache for the specified scope
 215    *
 216    * @param request The current request
 217    * @param scope The scope of this cache (<code>PageContext.APPLICATION_SCOPE</code>
 218    * or <code>PageContext.SESSION_SCOPE</code>)
 219    * @return The cache
 220    */
 221  0 public Cache getCache(HttpServletRequest request, int scope) {
 222  0 if (scope == PageContext.APPLICATION_SCOPE) {
 223  0 return getAppScopeCache(context);
 224    }
 225   
 226  0 if (scope == PageContext.SESSION_SCOPE) {
 227  0 return getSessionScopeCache(request.getSession(true));
 228    }
 229   
 230  0 throw new RuntimeException("The supplied scope value of " + scope + " is invalid. Acceptable values are PageContext.APPLICATION_SCOPE and PageContext.SESSION_SCOPE");
 231    }
 232   
 233    /**
 234    * A convenience method to retrieve the application scope cache
 235   
 236    * @param context the current <code>ServletContext</code>
 237    * @return the application scope cache. If none is present, one will
 238    * be created.
 239    */
 240  0 public Cache getAppScopeCache(ServletContext context) {
 241  0 Cache cache;
 242  0 Object obj = context.getAttribute(getCacheKey());
 243   
 244  0 if ((obj == null) || !(obj instanceof Cache)) {
 245  0 if (log.isInfoEnabled()) {
 246  0 log.info("Created new application-scoped cache at key: " + getCacheKey());
 247    }
 248   
 249  0 cache = createCache(PageContext.APPLICATION_SCOPE, null);
 250  0 context.setAttribute(getCacheKey(), cache);
 251    } else {
 252  0 cache = (Cache) obj;
 253    }
 254   
 255  0 return cache;
 256    }
 257   
 258    /**
 259    * A convenience method to retrieve the session scope cache
 260    *
 261    * @param session the current <code>HttpSession</code>
 262    * @return the session scope cache for this session. If none is present,
 263    * one will be created.
 264    */
 265  0 public Cache getSessionScopeCache(HttpSession session) {
 266  0 Cache cache;
 267  0 Object obj = session.getAttribute(getCacheKey());
 268   
 269  0 if ((obj == null) || !(obj instanceof Cache)) {
 270  0 if (log.isInfoEnabled()) {
 271  0 log.info("Created new session-scoped cache in session " + session.getId() + " at key: " + getCacheKey());
 272    }
 273   
 274  0 cache = createCache(PageContext.SESSION_SCOPE, session.getId());
 275  0 session.setAttribute(getCacheKey(), cache);
 276    } else {
 277  0 cache = (Cache) obj;
 278    }
 279   
 280  0 return cache;
 281    }
 282   
 283    /**
 284    * Get the cache key from the properties. Set it to a default value if it
 285    * is not present in the properties
 286    *
 287    * @return The cache.key property or the DEFAULT_CACHE_KEY
 288    */
 289  0 public String getCacheKey() {
 290  0 if (cacheKey == null) {
 291  0 cacheKey = getProperty(CACHE_KEY_KEY);
 292   
 293  0 if (cacheKey == null) {
 294  0 cacheKey = DEFAULT_CACHE_KEY;
 295    }
 296    }
 297   
 298  0 return cacheKey;
 299    }
 300   
 301    /**
 302    * Set the flush time for a specific scope to a specific time
 303    *
 304    * @param date The time to flush the scope
 305    * @param scope The scope to be flushed
 306    */
 307  0 public void setFlushTime(Date date, int scope) {
 308  0 if (log.isInfoEnabled()) {
 309  0 log.info("Flushing scope " + scope + " at " + date);
 310    }
 311   
 312  0 synchronized (flushTimes) {
 313  0 if (date != null) {
 314    // Trigger a SCOPE_FLUSHED event
 315  0 dispatchScopeEvent(ScopeEventType.SCOPE_FLUSHED, scope, date, null);
 316  0 flushTimes.put(new Integer(scope), date);
 317    } else {
 318  0 logError("setFlushTime called with a null date.");
 319  0 throw new IllegalArgumentException("setFlushTime called with a null date.");
 320    }
 321    }
 322    }
 323   
 324    /**
 325    * Set the flush time for a specific scope to the current time.
 326    *
 327    * @param scope The scope to be flushed
 328    */
 329  0 public void setFlushTime(int scope) {
 330  0 setFlushTime(new Date(), scope);
 331    }
 332   
 333    /**
 334    * Get the flush time for a particular scope.
 335    *
 336    * @param scope The scope to get the flush time for.
 337    * @return A date representing the time this scope was last flushed.
 338    * Returns null if it has never been flushed.
 339    */
 340  0 public Date getFlushTime(int scope) {
 341  0 synchronized (flushTimes) {
 342  0 return (Date) flushTimes.get(new Integer(scope));
 343    }
 344    }
 345   
 346    /**
 347    * Retrieve an item from the cache
 348    *
 349    * @param scope The cache scope
 350    * @param request The servlet request
 351    * @param key The key of the object to retrieve
 352    * @param refreshPeriod The time interval specifying if an entry needs refresh
 353    * @return The requested object
 354    * @throws NeedsRefreshException
 355    */
 356  0 public Object getFromCache(int scope, HttpServletRequest request, String key, int refreshPeriod) throws NeedsRefreshException {
 357  0 Cache cache = getCache(request, scope);
 358  0 key = this.generateEntryKey(key, request, scope);
 359  0 return cache.getFromCache(key, refreshPeriod);
 360    }
 361   
 362    /**
 363    * Checks if the given scope was flushed more recently than the CacheEntry provided.
 364    * Used to determine whether to refresh the particular CacheEntry.
 365    *
 366    * @param cacheEntry The cache entry which we're seeing whether to refresh
 367    * @param scope The scope we're checking
 368    *
 369    * @return Whether or not the scope has been flushed more recently than this cache entry was updated.
 370    */
 371  0 public boolean isScopeFlushed(CacheEntry cacheEntry, int scope) {
 372  0 Date flushDateTime = getFlushTime(scope);
 373   
 374  0 if (flushDateTime != null) {
 375  0 long lastUpdate = cacheEntry.getLastUpdate();
 376  0 return (flushDateTime.getTime() >= lastUpdate);
 377    } else {
 378  0 return false;
 379    }
 380    }
 381   
 382    /**
 383    * Register a listener for Cache Map events.
 384    *
 385    * @param listener The object that listens to events.
 386    */
 387  0 public void addScopeEventListener(ScopeEventListener listener) {
 388  0 listenerList.add(ScopeEventListener.class, listener);
 389    }
 390   
 391    /**
 392    * Cancels a pending cache update. This should only be called by a thread
 393    * that received a {@link NeedsRefreshException} and was unable to generate
 394    * some new cache content.
 395    *
 396    * @param scope The cache scope
 397    * @param request The servlet request
 398    * @param key The cache entry key to cancel the update of.
 399    */
 400  0 public void cancelUpdate(int scope, HttpServletRequest request, String key) {
 401  0 Cache cache = getCache(request, scope);
 402  0 key = this.generateEntryKey(key, request, scope);
 403  0 cache.cancelUpdate(key);
 404    }
 405   
 406    /**
 407    * Flush all scopes at a particular time
 408    *
 409    * @param date The time to flush the scope
 410    */
 411  0 public void flushAll(Date date) {
 412  0 synchronized (flushTimes) {
 413  0 setFlushTime(date, PageContext.APPLICATION_SCOPE);
 414  0 setFlushTime(date, PageContext.SESSION_SCOPE);
 415  0 setFlushTime(date, PageContext.REQUEST_SCOPE);
 416  0 setFlushTime(date, PageContext.PAGE_SCOPE);
 417    }
 418   
 419    // Trigger a flushAll event
 420  0 dispatchScopeEvent(ScopeEventType.ALL_SCOPES_FLUSHED, -1, date, null);
 421    }
 422   
 423    /**
 424    * Flush all scopes instantly.
 425    */
 426  0 public void flushAll() {
 427  0 flushAll(new Date());
 428    }
 429   
 430    /**
 431    * Generates a cache entry key.
 432    *
 433    * If the string key is not specified, the HTTP request URI and QueryString is used.
 434    * Operating systems that have a filename limitation less than 255 or have
 435    * filenames that are case insensitive may have issues with key generation where
 436    * two distinct pages map to the same key.
 437    * <p>
 438    * POST Requests (which have no distinguishing
 439    * query string) may also generate identical keys for what is actually different pages.
 440    * In these cases, specify an explicit key attribute for the CacheTag.
 441    *
 442    * @param key The key entered by the user
 443    * @param request The current request
 444    * @param scope The scope this cache entry is under
 445    * @return The generated cache key
 446    */
 447  0 public String generateEntryKey(String key, HttpServletRequest request, int scope) {
 448  0 return generateEntryKey(key, request, scope, null, null);
 449    }
 450   
 451    /**
 452    * Generates a cache entry key.
 453    *
 454    * If the string key is not specified, the HTTP request URI and QueryString is used.
 455    * Operating systems that have a filename limitation less than 255 or have
 456    * filenames that are case insensitive may have issues with key generation where
 457    * two distinct pages map to the same key.
 458    * <p>
 459    * POST Requests (which have no distinguishing
 460    * query string) may also generate identical keys for what is actually different pages.
 461    * In these cases, specify an explicit key attribute for the CacheTag.
 462    *
 463    * @param key The key entered by the user
 464    * @param request The current request
 465    * @param scope The scope this cache entry is under
 466    * @param language The ISO-639 language code to distinguish different pages in application scope
 467    * @return The generated cache key
 468    */
 469  0 public String generateEntryKey(String key, HttpServletRequest request, int scope, String language) {
 470  0 return generateEntryKey(key, request, scope, language, null);
 471    }
 472   
 473    /**
 474    * Generates a cache entry key.
 475    * <p>
 476    * If the string key is not specified, the HTTP request URI and QueryString is used.
 477    * Operating systems that have a filename limitation less than 255 or have
 478    * filenames that are case insensitive may have issues with key generation where
 479    * two distinct pages map to the same key.
 480    * <p>
 481    * POST Requests (which have no distinguishing
 482    * query string) may also generate identical keys for what is actually different pages.
 483    * In these cases, specify an explicit key attribute for the CacheTag.
 484    *
 485    * @param key The key entered by the user
 486    * @param request The current request
 487    * @param scope The scope this cache entry is under
 488    * @param language The ISO-639 language code to distinguish different pages in application scope
 489    * @param suffix The ability to put a suffix at the end of the key
 490    * @return The generated cache key
 491    */
 492  0 public String generateEntryKey(String key, HttpServletRequest request, int scope, String language, String suffix) {
 493    /**
 494    * Used for generating cache entry keys.
 495    */
 496  0 StringBuffer cBuffer = new StringBuffer(AVERAGE_KEY_LENGTH);
 497   
 498    // Append the language if available
 499  0 if (language != null) {
 500  0 cBuffer.append(FILE_SEPARATOR).append(language);
 501    }
 502   
 503    // Servers for multiple host domains need this distinction in the key
 504  0 if (useHostDomainInKey) {
 505  0 cBuffer.append(FILE_SEPARATOR).append(request.getServerName());
 506    }
 507   
 508  0 if (key != null) {
 509  0 cBuffer.append(FILE_SEPARATOR).append(key);
 510    } else {
 511  0 String generatedKey = request.getRequestURI();
 512   
 513  0 if (generatedKey.charAt(0) != FILE_SEPARATOR_CHAR) {
 514  0 cBuffer.append(FILE_SEPARATOR_CHAR);
 515    }
 516   
 517  0 cBuffer.append(generatedKey);
 518  0 cBuffer.append("_").append(request.getMethod()).append("_");
 519   
 520  0 generatedKey = getSortedQueryString(request);
 521   
 522  0 if (generatedKey != null) {
 523  0 try {
 524  0 java.security.MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
 525  0 byte[] b = digest.digest(generatedKey.getBytes());
 526  0 cBuffer.append("_");
 527   
 528    // Base64 encoding allows for unwanted slash characters.
 529  0 cBuffer.append(toBase64(b).replace('/', '_'));
 530    } catch (Exception e) {
 531    // Ignore query string
 532    }
 533    }
 534    }
 535   
 536    // Do we want a suffix
 537  0 if ((suffix != null) && (suffix.length() > 0)) {
 538  0 cBuffer.append(suffix);
 539    }
 540   
 541  0 return cBuffer.toString();
 542    }
 543   
 544    /**
 545    * Creates a string that contains all of the request parameters and their
 546    * values in a single string. This is very similar to
 547    * <code>HttpServletRequest.getQueryString()</code> except the parameters are
 548    * sorted by name, and if there is a <code>jsessionid</code> parameter it is
 549    * filtered out.<p>
 550    * If the request has no parameters, this method returns <code>null</code>.
 551    */
 552  0 protected String getSortedQueryString(HttpServletRequest request) {
 553  0 Map paramMap = request.getParameterMap();
 554   
 555  0 if (paramMap.isEmpty()) {
 556  0 return null;
 557    }
 558   
 559  0 Set paramSet = new TreeMap(paramMap).entrySet();
 560   
 561  0 StringBuffer buf = new StringBuffer();
 562   
 563  0 boolean first = true;
 564   
 565  0 for (Iterator it = paramSet.iterator(); it.hasNext();) {
 566  0 Map.Entry entry = (Map.Entry) it.next();
 567  0 String[] values = (String[]) entry.getValue();
 568   
 569  0 for (int i = 0; i < values.length; i++) {
 570  0 String key = (String) entry.getKey();
 571   
 572  0 if ((key.length() != 10) || !"jsessionid".equals(key)) {
 573  0 if (first) {
 574  0 first = false;
 575    } else {
 576  0 buf.append('&');
 577    }
 578   
 579  0 buf.append(key).append('=').append(values[i]);
 580    }
 581    }
 582    }
 583   
 584    // We get a 0 length buffer if the only parameter was a jsessionid
 585  0 if (buf.length() == 0) {
 586  0 return null;
 587    } else {
 588  0 return buf.toString();
 589    }
 590    }
 591   
 592    /**
 593    * Log error messages to commons logging.
 594    *
 595    * @param message Message to log.
 596    */
 597  0 public void logError(String message) {
 598  0 log.error("[oscache]: " + message);
 599    }
 600   
 601    /**
 602    * Put an object in the cache
 603    *
 604    * @param scope The cache scope
 605    * @param request The servlet request
 606    * @param key The object key
 607    * @param content The object to add
 608    */
 609  0 public void putInCache(int scope, HttpServletRequest request, String key, Object content) {
 610  0 putInCache(scope, request, key, content, null);
 611    }
 612   
 613    /**
 614    * Put an object in the cache
 615    *
 616    * @param scope The cache scope
 617    * @param request The servlet request
 618    * @param key The object key
 619    * @param content The object to add
 620    * @param policy The refresh policy
 621    */
 622  0 public void putInCache(int scope, HttpServletRequest request, String key, Object content, EntryRefreshPolicy policy) {
 623  0 Cache cache = getCache(request, scope);
 624  0 key = this.generateEntryKey(key, request, scope);
 625  0 cache.putInCache(key, content, policy);
 626    }
 627   
 628    /**
 629    * Sets the cache capacity (number of items). If the cache contains
 630    * more than <code>capacity</code> items then items will be removed
 631    * to bring the cache back down to the new size.
 632    *
 633    * @param scope The cache scope
 634    * @param request The servlet request
 635    * @param capacity The new capacity
 636    */
 637  0 public void setCacheCapacity(int scope, HttpServletRequest request, int capacity) {
 638  0 setCacheCapacity(capacity);
 639  0 getCache(request, scope).setCapacity(capacity);
 640    }
 641   
 642    /**
 643    * Unregister a listener for Cache Map events.
 644    *
 645    * @param listener The object that currently listens to events.
 646    */
 647  0 public void removeScopeEventListener(ScopeEventListener listener) {
 648  0 listenerList.remove(ScopeEventListener.class, listener);
 649    }
 650   
 651    /**
 652    * Finalizes all the listeners that are associated with the given cache object
 653    */
 654  0 protected void finalizeListeners(Cache cache) {
 655  0 super.finalizeListeners(cache);
 656    }
 657   
 658    /**
 659    * Convert a byte array into a Base64 string (as used in mime formats)
 660    */
 661  0 private static String toBase64(byte[] aValue) {
 662  0 int byte1;
 663  0 int byte2;
 664  0 int byte3;
 665  0 int iByteLen = aValue.length;
 666  0 StringBuffer tt = new StringBuffer();
 667   
 668  0 for (int i = 0; i < iByteLen; i += 3) {
 669  0 boolean bByte2 = (i + 1) < iByteLen;
 670  0 boolean bByte3 = (i + 2) < iByteLen;
 671  0 byte1 = aValue[i] & 0xFF;
 672  0 byte2 = (bByte2) ? (aValue[i + 1] & 0xFF) : 0;
 673  0 byte3 = (bByte3) ? (aValue[i + 2] & 0xFF) : 0;
 674   
 675  0 tt.append(m_strBase64Chars.charAt(byte1 / 4));
 676  0 tt.append(m_strBase64Chars.charAt((byte2 / 16) + ((byte1 & 0x3) * 16)));
 677  0 tt.append(((bByte2) ? m_strBase64Chars.charAt((byte3 / 64) + ((byte2 & 0xF) * 4)) : '='));
 678  0 tt.append(((bByte3) ? m_strBase64Chars.charAt(byte3 & 0x3F) : '='));
 679    }
 680   
 681  0 return tt.toString();
 682    }
 683   
 684    /**
 685    * Create a cache
 686    *
 687    * @param scope The cache scope
 688    * @param sessionId The sessionId for with the cache will be created
 689    * @return A new cache
 690    */
 691  0 private ServletCache createCache(int scope, String sessionId) {
 692  0 ServletCache newCache = new ServletCache(this, algorithmClass, cacheCapacity, scope);
 693   
 694    // TODO - Fix me please!
 695    // Hack! This is nasty - if two sessions are created within a short
 696    // space of time it is possible they will end up with duplicate
 697    // session IDs being passed to the DiskPersistenceListener!...
 698  0 config.set(HASH_KEY_SCOPE, "" + scope);
 699  0 config.set(HASH_KEY_SESSION_ID, sessionId);
 700   
 701  0 newCache = (ServletCache) configureStandardListeners(newCache);
 702   
 703  0 if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) {
 704    // Add any event listeners that have been specified in the configuration
 705  0 CacheEventListener[] listeners = getCacheEventListeners();
 706   
 707  0 for (int i = 0; i < listeners.length; i++) {
 708  0 if (listeners[i] instanceof ScopeEventListener) {
 709  0 newCache.addCacheEventListener(listeners[i], ScopeEventListener.class);
 710    }
 711    }
 712    }
 713   
 714  0 return newCache;
 715    }
 716   
 717    /**
 718    * Dispatch a scope event to all registered listeners.
 719    *
 720    * @param eventType The type of event
 721    * @param scope Scope that was flushed (Does not apply for FLUSH_ALL event)
 722    * @param date Date of flushing
 723    * @param origin The origin of the event
 724    */
 725  0 private void dispatchScopeEvent(ScopeEventType eventType, int scope, Date date, String origin) {
 726    // Create the event
 727  0 ScopeEvent event = new ScopeEvent(eventType, scope, date, origin);
 728   
 729    // Guaranteed to return a non-null array
 730  0 Object[] listeners = listenerList.getListenerList();
 731   
 732    // Process the listeners last to first, notifying
 733    // those that are interested in this event
 734  0 for (int i = listeners.length - 2; i >= 0; i -= 2) {
 735  0 if (listeners[i] == ScopeEventListener.class) {
 736  0 ((ScopeEventListener) listeners[i + 1]).scopeFlushed(event);
 737    }
 738    }
 739    }
 740   
 741    /**
 742    * Set property cache.use.host.domain.in.key=true to add domain information to key
 743    * generation for hosting multiple sites
 744    */
 745  0 private void initHostDomainInKey() {
 746  0 String propStr = getProperty(CACHE_USE_HOST_DOMAIN_KEY);
 747   
 748  0 useHostDomainInKey = "true".equalsIgnoreCase(propStr);
 749    }
 750    }