Package org.w3c.www.protocol.http.cache

Source Code of org.w3c.www.protocol.http.cache.CacheGeneration

// CacheGeneration.java
// $Id: CacheGeneration.java,v 1.37 2007/02/09 22:16:38 ylafon Exp $
// (c) COPYRIGHT MIT, INRIA and Keio, 1999.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.www.protocol.http.cache;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import java.io.File;
import java.io.PrintStream;

import org.w3c.util.LRUAble;
import org.w3c.util.LRUList;
import org.w3c.util.LookupTable;
import org.w3c.util.SyncLRUList;

public class CacheGeneration implements LRUAble {
    // the usual debug flag
    private static final boolean debug = true;

    // hashtable of resources
    private Hashtable lookupTable = null;
    // the LRUList of resources
    private SyncLRUList lruList = null;
    // the occupation of this generation
    private long bytecount = 0;
    // the capacity of this generation
    private long bytelimit = 0;
    // stored size
    private long bytestored = 0;
    // serialization flag
    private boolean saved = false;
    // serialization flag
    private boolean loaded = false;
    // resource count
    private int cr_count = 0;
    // the ID of this generation
    private int id = 0;

    // a Vector or resource to be removed
    private Vector toDel = null;

    // our father
    private CacheStore store = null;

    protected File generationFile = null;

    /**
     * set the file where the generation is stored
     * @param generationFile the file
     */
    public void setGenerationFile(File generationFile) {
  this.generationFile = generationFile;
    }

    /**
     * get the generation file
     * @return a File
     */
    public File getGenerationFile() {
  return generationFile;
    }

    /**
     * Is the generation loaded?
     * @return a boolean
     */
    public boolean isLoaded() {
  return loaded;
    }

    /**
     * Set the generation as loaded or unloaded
     * @param loaded the new loaded flag
     */
    protected void setLoaded(boolean loaded) {
  this.loaded = loaded;
    }

    /**
     * Is the generation saved?
     * @return a boolean
     */
    public boolean isSaved() {
  return saved;
    }

    /**
     * Set the generation as saved or not.
     * @param saved a boolean
     */
    protected void setSaved(boolean saved) {
  this.saved = saved;
    }

    /**
     * LRU management - previous entry.
     */
    protected LRUAble prev = null;
    /**
     * LRU management - next entry.
     */
    protected LRUAble next = null;

    /**
     * LRU management - Get next node.
     * @return A CacheGeneration instance.
     */
    public LRUAble getNext() {
  return next;
    }

    /**
     * LRU management - Get previous node.
     * @return A CacheGeneration instance.
     */
    public LRUAble getPrev() {
  return prev;
    }

    /**
     * LRU management - Set next node.
     */
    public synchronized void setNext(LRUAble next) {
  this.next = next;
    }

    /**
     * LRU management - Set previous node.
     */
    public synchronized void setPrev(LRUAble prev) {
  this.prev = prev;
    }

    /**
     * Get the ID of this generation
     * @return an int, the generation number
     */
    public int getId() {
  return id;
    }

    /**
     * Set the ID of this generation
     * Useful to reuse generation
     * @param an integer, the new generation number
     */
    public synchronized void setId(int id) {
  this.id = id;
    }

    /**
     * Give the acual occupation level of this generation
     * @return a long, the number of bytes of this generation
     */
    public long getCachedByteCount() {
  return bytecount;
    }

    /**
     * Give the fill ratio for the cached resources
     * @return a float between 0 and 1
     */
    public float getFillRatio() {
  return ((float) bytecount / (float) bytelimit);
    }

    /**
     * Give the acual storeage occupation level of this generation
     * @return a long, the number of bytes of this generation
     */
    public long getStoredByteCount() {
  return bytestored;
    }

    /**
     * Get the bytecount limit for this generation
     * @return a long, the maximum number of bytes
     */
    public long getByteLimit() {
  return bytelimit;
    }

    /**
     * Set the new bytecount limit, not that it may perform a cleanup
     * if necessary.
     * @param long, the new maximum number of bytes
     */
    public synchronized void setByteLimit(long newlimit) {
  bytelimit = newlimit;
  if (newlimit >= bytecount) {
      return;
  }
  // try to get some space
  long to_save = newlimit - bytecount;
  // be nice
  to_save -= collectSpace(newlimit - bytecount, true);
  // then get the space we want ;)
  to_save -= collectSpace(to_save, false);
    }

    /**
     * Deletes a resource from the "to be deleted" vector
     * it updates also the number of bye stored in this generation
     * @return the number of bytes saved.
     */
    public long deleteStored(CachedResource cr) {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  if (debug) {
      System.out.println("Deleting "+cr.getIdentifier()+
             "from generation: "+id);
  }
  toDel.removeElement(cr);
  long saved = cr.delete();
  synchronized(this) {
      bytestored -= saved;
  }
  store.getState().notifyResourceDeleted(cr);
  return saved;
    }

    /**
     * Check if a resource has been cached in this generation
     * @param url the resource url
     * @return a boolean
     */
    public synchronized boolean containsResource(String url) {
  return (lookupTable.get(url) != null);
    }

    /**
     * Get all the files handled by this generation
     * @return an enumeration of File
     */
    public synchronized Enumeration getFiles() {
  Vector files = new Vector();
  if (loaded) {
      Enumeration fenum = lookupTable.elements();
      while (fenum.hasMoreElements()) {
    CachedResource cr = (CachedResource) fenum.nextElement();
    File file = cr.getFile();
    if (file != null) {
        files.addElement(file);
    }
      }
  } else {
      Enumeration fenum = lookupTable.elements();
      while (fenum.hasMoreElements()) {
    String file = (String) fenum.nextElement();
    if (! file.equals("")) {
        files.addElement(new File(file));
    }
      }
  }
  return files.elements();
    }

    /**
     * Get the CachedResource relative to the given URL.
     * @param url the URL of the CachedResource to find
     * @return a CachedResource or null.
     */
    public synchronized CachedResource lookupResource(String url) {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  return (CachedResource)lookupTable.get(url);
    }

    /**
     * can this resource be stored?
     * If the resource is in the generation, only the delta will be taken
     * into account
     * @param CachedResource cr, the candidate.
     * @param long size, the size of the candidate.
     * @return a boolean, if this generation can cache it or not
     */
    private boolean canStore(CachedResource cr, long size) {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  CachedResource old_cr = null;
  old_cr = (CachedResource) lookupTable.get(cr.getIdentifier());
  if (old_cr != null) {
      long delta = size - old_cr.getCurrentLength();
      if ( (bytecount + delta) > bytelimit) {
    return false;
      }
  }
  if ((size + bytecount) > bytelimit) {
      return false;
  }
  return true;
    }

    /**
     * Adds this resource, if possible
     * @param cr, the candidate.
     * @param size, the size of the candidate.
     * @return a boolean, true if this resource has been cached
     */
    public synchronized boolean addResource(CachedResource cr,
              long size,
              long oldsize)
    {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  if (canStore(cr, size)) {
      CachedResource old_cr = null;
      old_cr = (CachedResource) lookupTable.get(cr.getIdentifier());
      // do we already have this resource?
      if (old_cr != null) {
    // this is the real oldsize
    oldsize = old_cr.getCurrentLength();
    long delta   = size - oldsize;
    if  ((bytecount + delta) > bytelimit) {
        return false;
    }
    lookupTable.remove(cr.getIdentifier());
    lruList.remove(cr);
    bytecount -= oldsize;
    toDel.addElement(old_cr);
    store.getState().notifyResourceReplaced(cr, oldsize);
      } else {
    store.getState().notifyResourceAdded(cr, oldsize);
      }
      lookupTable.put(cr.getIdentifier(), cr);
      cr.generation = this;
      lruList.toHead(cr);
      bytestored += size;
      bytecount  += size;
      saved       = false;
      cr_count++;
      return true;
  }
  return false;
    }

    /**
     * Load a CachedResource in this generation. (to be used only at
     * generation loading). This method load only valid cachedresource.
     * Check if the associated file exists and has the right size.
     * @param CachedResource the CachedResource to load.
     */
    protected void loadCachedResource(CachedResource cr) {
  File file = cr.getFile();
  if ((file != null) &&
      ((! file.exists()) || (file.length() != cr.getCurrentLength()))) {
      //don't load invalid cachedresource
      return;
  }
  lookupTable.put(cr.getIdentifier(), cr);
  cr.generation = this;
  lruList.toHead(cr);
  long size   = cr.getCurrentLength();
  bytestored += size;
  bytecount  += size;
  cr_count++;
    }

    /**
     * Remove the resource from the generation (but don't delete it).
     * @param cr the CachedResource to remove.
     * @return the number of byte saved
     * @exception NoSuchResourceException if this resource was not in this
     * generation    
     */
    public synchronized long removeResource(CachedResource cr)
  throws NoSuchResourceException
    {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  return removeResource(cr.getIdentifier());
    }

    /**
     * Remove the resource from the generation (but don't delete it).
     * @param cr the CachedResource to remove.
     * @return the number of byte saved
     * @exception NoSuchResourceException if this resource was not in this
     * generation    
     */
    public synchronized long removeResource(String url)
  throws NoSuchResourceException
    {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  CachedResource old_cr = _removeResource(url);
  return old_cr.getCurrentLength();
    }

    /**
     * Remove the resource from the generation and update the bytecount
     * variable.
     * @param url the CachedResource URL
     * @return the CachedResource removed.
     * @exception NoSuchResourceException if this resource was not in this
     * generation
     */
    private CachedResource _removeResource(String url)
  throws NoSuchResourceException
    {
  if (debug) {
      System.err.println("*** removing from generation "+id+": " + url);
  }
  CachedResource old_cr = (CachedResource) lookupTable.get(url);
  if (old_cr == null) {
      String msg = url + " not found in generation " + id;
      throw new NoSuchResourceException(msg);
  }
  lookupTable.remove(url);
  lruList.remove(old_cr);
  long b_saved = old_cr.getCurrentLength();
  if (debug) {
      System.err.println("*** removed... saved " + b_saved + " bytes");
  }
  bytecount  -= b_saved;
  bytestored -= b_saved;
  saved = false;
  cr_count--;
  return old_cr;
    }

    /**
     * will garbage collect up to "size" bytes in this generation.
     * WARNING: this is not synchronized, use with caution!
     * @param long the number of bytes to be collected
     * @param check, a boolean, used to validate or not the resource before
     * deleting them (ie: delete only invalid resources)
     * @return a long, the number of bytes saved
     * from disk afterward using delete.
     */

    public long collectSpace(long size, boolean check) {
  if (! loaded) {
      // load me, and unload another generation is necessary.
      try {
    store.loadGeneration(this);
      } catch (InvalidCacheException ex) {
    // oups! cache corrupted?
    if (debug) {
        System.err.println("*** Collecting "+
               "Unable to load generation ["+
               getId()+"]");
        System.err.println(ex.getMessage());
    }
    return 0;
      }
  }
  Vector res_vect = new Vector();
  long collected = 0;
  CachedResource cr, ncr;
  CacheValidator validator;
 
  if (debug) {
      System.err.println("*** Collecting " + size + " bytes " +
             ((check) ? "with" : "without") + " checking");
  }
  // dumb check
  if (size <= 0)
      return 0;
  if ((size > bytecount) && !check) {
      return emptyGeneration();
  }
  validator = store.getValidator();
  // start with the oldest ones
  cr = (CachedResource) lruList.getTail();
  while (cr != null) {
      // check if we can delete the resource or not
      if (check) {
    if (validator.checkStaleness(cr)) {
        cr = (CachedResource) lruList.getPrev(cr);
        continue;
    }
      }
      ncr = (CachedResource) lruList.getPrev(cr);
      synchronized(this) {
    lookupTable.remove(cr.getIdentifier());
    lruList.remove(cr);
    saved = false;
    cr_count--;
    store.getState().notifyResourceToBeDeleted(cr);
      }
      collected += cr.getCurrentLength();
      toDel.addElement(cr);
      if (collected >= size) {
    break;
      }
      cr = ncr;
  }
  synchronized (this) {
      bytecount -= collected;
  }
  if (debug) {
      System.err.println("*** Collected " + collected +
             " bytes from generation "+id);
  }
  return collected;
    }

    /**
     * empty this generation
     * @return a long, the number of bytes saved
     */
    protected long emptyGeneration() {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  Hashtable saved_table;
  long collected;
  if (debug) {
      System.err.println("*** Deleting Generation " + id + " ("
             + bytecount + ")");
  }
  synchronized (this) {
      collected = bytecount;
      saved_table = lookupTable;
      lookupTable = new Hashtable();
      lruList = new SyncLRUList();
      bytecount = 0;
      cr_count = 0;
  }
  Enumeration e = saved_table.elements();
  while (e.hasMoreElements()) {
      CachedResource cr = (CachedResource) e.nextElement();
      toDel.addElement(cr);
      store.getState().notifyResourceToBeDeleted(cr);
  }
  generationFile.delete();
  saved = false;
  return collected;
    }

    /**
     * Get the CachedResource of this generation (except the "to be
     * deleted" resources)
     * @return an Enumeration of CachedResource
     */
    public Enumeration getCachedResources() {
  if (! loaded)
      throw new UnloadedGenerationException("generation "+id);
  return lookupTable.elements();
    }

    /**
     * get the deleted but still stored resource
     * @returns an enumeration of CachedResources
     */
    public Enumeration getDeletedResources() {
  return toDel.elements();
    }

    /**
     * Set this Generation as a description (update the saved and loaded
     * status)
     * @param tables the LookupTables containing attribute descriptions
     */
    protected void setDescription(LookupTable tables[]) {
  clean();
  saved      = true;
  bytestored = 0;
  bytecount  = 0;
  cr_count   = 0;
  for (int i = 0 ; i < tables.length ; i++) {
      LookupTable table = tables[i];
      try {
    String stored =
        (String)table.get(CachedResource.NAME_CURRENT_LENGTH);
    String id =
        (String)table.get(CachedResource.NAME_IDENTIFIER);
    String file =
        (String)table.get(CachedResource.NAME_FILE);
    file = (file == null) ? "" : file;
    bytestored += Integer.parseInt(stored);
    lookupTable.put(id, file);
      } catch (Exception ex) {
    if (debug) {
        System.err.println("Unable to load description in ["+
               getId()+"] "+ex.getMessage());
    }
      }
  }
    }

    /**
     * Unload the generation, transform CachedResources to descriptions.
     */
    public void unload() {
  // bytestored unchanged
  clean();
  saved             = true;
  bytecount         = 0;
  cr_count          = 0;
  Enumeration kenum = lookupTable.keys();
  while(kenum.hasMoreElements()) {
      lookupTable.put(kenum.nextElement(), Boolean.TRUE);
  }
    }

    /**
     * delete the serialized resource file from the disk
     */
    protected void deleteGenerationFile() {
  generationFile.delete();
    }

    /**
     * Get the current number of resource loaded.
     * @return an long
     */
    public int getCRCount() {
  return cr_count;
    }

    /**
     * Clean this generation.
     */
    public void clean() {
  this.lookupTable = new Hashtable();
  this.lruList     = new SyncLRUList();
  this.loaded      = false;
    }

    /**
     * copy the content of the generation here
     * @parameter a CacheGeneration, the one we want to dump here
     */
    protected synchronized void copyInto(CacheGeneration gen) {
  // add everything at the "right" place
  LRUList ngenLruList = gen.lruList;
  CachedResource cr = (CachedResource) ngenLruList.getHead();
  while (cr != null) {
      CachedResource next_cr = (CachedResource) ngenLruList.getNext(cr);
      cr.generation = this;
      lruList.toTail(cr);
      lookupTable.put(cr.getIdentifier(), cr);
      cr = next_cr;
  }
  gen.setSaved(false);
  // now dump the toDel vector
  Enumeration e = gen.toDel.elements();
  while (e.hasMoreElements()) {
      toDel.add(e.nextElement());
  }
  // update the values
  cr_count += gen.cr_count;
  bytecount += gen.bytecount;
  bytestored += gen.bytestored;
  // finally state that we have been modified
  saved = false;
    }

    public CacheGeneration(CacheStore store, long maxsize) {
  this.store       = store;
  this.toDel       = new Vector();
  this.lookupTable = new Hashtable();
  this.bytelimit   = maxsize;
  this.lruList     = new SyncLRUList();
    }
}

TOP

Related Classes of org.w3c.www.protocol.http.cache.CacheGeneration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.