You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by fr...@apache.org on 2002/02/08 00:37:35 UTC

cvs commit: xml-cocoon2/src/scratchpad/src/org/apache/cocoon/storejanitor StoreJanitorImpl.java

froehlich    02/02/07 15:37:35

  Added:       src/scratchpad/src/org/apache/cocoon/storejanitor
                        StoreJanitorImpl.java
  Log:
  added re-factored StoreJanitor from Peter Hargreaves [pdhargr@netscape.net]
  into the scratchpad.
  Config is coming soon.
  
  Revision  Changes    Path
  1.1                  xml-cocoon2/src/scratchpad/src/org/apache/cocoon/storejanitor/StoreJanitorImpl.java
  
  Index: StoreJanitorImpl.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache Cocoon" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
   
  package org.apache.cocoon.storejanitor;
  
  import org.apache.avalon.framework.activity.Startable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.cocoon.components.store.StoreJanitor;
  import org.apache.cocoon.components.store.Store;
  
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.Iterator;
  
  /**
   * This class is a implentation of a StoreJanitor. Store classes
   * can register to the StoreJanitor. When memory is too low,
   * the StoreJanitor frees the registered caches until memory is normal.
   *
   *@author     <a href="mailto:cs@ffzj0ia9.bank.dresdner.net">Christian Schmitt</a>
   *@author     <a href="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
   *@author     <a href="mailto:proyal@managingpartners.com">Peter Royal</a>
   *@author     <a href="mailto:pdh@totalise.co.uk">Peter Hargreaves</a>
   *
   *@version CVS $Id: StoreJanitorImpl.java,v 1.1 2002/02/07 23:37:35 froehlich Exp $
   */
  public class StoreJanitorImpl
           extends AbstractLoggable
           implements StoreJanitor,
                      Configurable,
                      ThreadSafe,
                      Runnable,
                      Startable {
      private static boolean doRun = false;
  
      private int freememory = -1;
      private int heapsize = -1;
      private int cleanupthreadinterval = -1;
      private int priority = -1;
      private Runtime jvm;
      private ArrayList storelist;
      private int index = -1;
      private int m_percent;
  
      private long inUseAfter;// Remember while sleeping.
      private long sleepPeriod;// Remember while sleeping.
      private long maxRateOfChange;
  
      /**
       * Initialize the StoreJanitorImpl.
       * A few options can be used :
       * <UL>
       *  <LI>freememory = how many bytes shall be always free in the jvm</LI>
       *  <LI>heapsize = max. size of jvm memory consumption</LI>
       *  <LI>cleanupthreadinterval = how often (sec) shall run the cleanup thread</LI>
       *  <LI>threadpriority = priority of the thread (1-10). (Default: 10)</LI>
       * </UL>
       *
       *@param  conf                        Description of Parameter
       *@exception  ConfigurationException
       *@since
       */
      public void configure(Configuration conf) throws ConfigurationException {
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Configure StoreJanitorImpl");
          }
          this.setJVM(Runtime.getRuntime());
  
          Parameters params = Parameters.fromConfiguration(conf);
          this.setFreememory(params.getParameterAsInteger("freememory", 1000000));
          this.setHeapsize(params.getParameterAsInteger("heapsize", 60000000));
          this.setCleanupthreadinterval(params.getParameterAsInteger("cleanupthreadinterval", 10));
          this.setPriority(params.getParameterAsInteger("threadpriority",
                  Thread.currentThread().getPriority()));
          this.m_percent = params.getParameterAsInteger("percent_to_free", 10);
  
          if ((this.getFreememory() < 1)) {
              throw new ConfigurationException("StoreJanitorImpl freememory parameter has to be greater then 1");
          }
          if ((this.getHeapsize() < 1)) {
              throw new ConfigurationException("StoreJanitorImpl heapsize parameter has to be greater then 1");
          }
          if ((this.getCleanupthreadinterval() < 1)) {
              throw new ConfigurationException("StoreJanitorImpl cleanupthreadinterval parameter has to be greater then 1");
          }
          if ((this.getPriority() < 1)) {
              throw new ConfigurationException("StoreJanitorImpl threadpriority has to be greater then 1");
          }
          if ((this.m_percent > 100 && this.m_percent < 1)) {
              throw new ConfigurationException("StoreJanitorImpl percent_to_free, has to be between 1 and 100");
          }
  
          this.setStoreList(new ArrayList());
      }
  
      /**
       *Description of the Method
       *
       *@since
       */
      public void start() {
          doRun = true;
          Thread checker = new Thread(this);
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Intializing checker thread");
          }
          checker.setPriority(this.getPriority());
          checker.setDaemon(true);
          checker.setName("checker");
          checker.start();
      }
  
      /**
       *Description of the Method
       *
       *@since
       */
      public void stop() {
          doRun = false;
      }// Remember while sleeping.
  
      /**
       * The "checker" thread checks if items should be removed from the stores.
       *
       *@since
       */
      public void run() {
          // Initialise for a safe first calculation of rate of change.
          inUseAfter = inUseNow();
          sleepPeriod = Long.MAX_VALUE;
          maxRateOfChange = 1;
          while (doRun) {
              // Monitor the rate of change of heap in use.
              long changeWhileSleeping = inUseNow() - inUseAfter;
              long rateOfChange = longDiv(changeWhileSleeping, sleepPeriod);// bpms (same as or kbps).
              if (maxRateOfChange < rateOfChange) {
                  maxRateOfChange = (maxRateOfChange + rateOfChange) / 2;
              }
              // Output debug info.
              debug("Waking after " + sleepPeriod + "ms, in use change " + changeWhileSleeping + " to " + inUseNow() + ", rate " + rateOfChange + "kb/sec, max rate " + maxRateOfChange + "kb/sec");
              debug("maxHeap=" + getMaxHeap() + ", totalHeap=" + jvm.totalMemory() + ", heapIsBig=" + heapIsBig());
              debug("minFree=" + getMinFree() + ",  freeHeap=" + jvm.freeMemory() + ", freeIsLow=" + freeIsLow());
              // If the heap is big, and the free memory is low.
              if (heapIsBig() && freeIsLow()) {
                  synchronized (this) {
                      attemptToFreeStorage();
                  }
              }
              // Remember memory in use before sleeping in order to calc slope when waking.
              inUseAfter = inUseNow();
              // If time to half fill could be less than max sleep, then sleep for half min time to fill (& remember it to calc slope next time).
              sleepPeriod = minTimeToFill() / 2 < getMaxSleep() ? minTimeToFill() / 2 : getMaxSleep();
              debug("Store checker going to sleep for " + sleepPeriod + "ms, (max sleep=" + getMaxSleep() + "ms), with memory in use=" + inUseAfter);
              try {
                  Thread.currentThread().sleep(sleepPeriod);
              } catch (InterruptedException ignore) {
              }
          }
      }
  
      /**
       * This method register the stores
       *
       *@param  store  Description of Parameter
       *@since
       */
      public void register(Store store) {
          this.getStoreList().add(store);
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Registering store instance");
              this.getLogger().debug("Size of StoreJanitor now:"
                       + this.getStoreList().size());
          }
      }
  
      /**
       * This method unregister the stores
       *
       *@param  store  Description of Parameter
       *@since
       */
      public void unregister(Store store) {
          this.getStoreList().remove(store);
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug("Unregister store instance");
              this.getLogger().debug("Size of StoreJanitor now:"
                       + this.getStoreList().size());
          }
      }
  
      /**
       * This method return a java.util.Iterator of every registered stores
       *
       * <i>The iterators returned is fail-fast: if list is structurally
       * modified at any time after the iterator is created, in any way, the
       * iterator will throw a ConcurrentModificationException.  Thus, in the
       * face of concurrent modification, the iterator fails quickly and
       * cleanly, rather than risking arbitrary, non-deterministic behavior at
       * an undetermined time in the future.</i>
       *
       *@return    a java.util.Iterator
       *@since
       */
      public Iterator iterator() {
          return this.getStoreList().iterator();
      }
  
      /**
       *Sets the freememory attribute of the StoreJanitorImpl object
       *
       *@param  _freememory  The new freememory value
       *@since
       */
      private void setFreememory(int _freememory) {
          this.freememory = _freememory;
      }
  
      /**
       *Sets the heapsize attribute of the StoreJanitorImpl object
       *
       *@param  _heapsize  The new heapsize value
       *@since
       */
      private void setHeapsize(int _heapsize) {
          this.heapsize = _heapsize;
      }
  
      /**
       *Sets the cleanupthreadinterval attribute of the StoreJanitorImpl object
       *
       *@param  _cleanupthreadinterval  The new cleanupthreadinterval value
       *@since
       */
      private void setCleanupthreadinterval(int _cleanupthreadinterval) {
          this.cleanupthreadinterval = _cleanupthreadinterval;
      }
  
      /**
       *Sets the priority attribute of the StoreJanitorImpl object
       *
       *@param  _priority  The new priority value
       *@since
       */
      private void setPriority(int _priority) {
          this.priority = _priority;
      }
  
      /**
       *Sets the jVM attribute of the StoreJanitorImpl object
       *
       *@param  _runtime  The new jVM value
       *@since
       */
      private void setJVM(Runtime _runtime) {
          this.jvm = _runtime;
      }
  
      /**
       *Sets the storeList attribute of the StoreJanitorImpl object
       *
       *@param  _storelist  The new storeList value
       *@since
       */
      private void setStoreList(ArrayList _storelist) {
          this.storelist = _storelist;
      }
  
      // Renaming of parameters due to new functionality.
      /**
       *Gets the maxHeap attribute of the StoreJanitorImpl object
       *
       *@return    The maxHeap value
       *@since
       */
      private int getMaxHeap() {
          return heapsize;
      }
  
      /**
       *Gets the minFree attribute of the StoreJanitorImpl object
       *
       *@return    The minFree value
       *@since
       */
      private int getMinFree() {
          return freememory;
      }
  
      /**
       *Gets the maxSleep attribute of the StoreJanitorImpl object
       *
       *@return    The maxSleep value
       *@since
       */
      private int getMaxSleep() {
          return cleanupthreadinterval * 1000;
      }
  
      /**
       *Gets the reduceBy attribute of the StoreJanitorImpl object
       *
       *@return    The reduceBy value
       *@since
       */
      private int getReduceBy() {
          return m_percent;
      }
  
      /**
       *Gets the freememory attribute of the StoreJanitorImpl object
       *
       *@return    The freememory value
       *@since
       */
      private int getFreememory() {
          return freememory;
      }
  
      /**
       *Gets the heapsize attribute of the StoreJanitorImpl object
       *
       *@return    The heapsize value
       *@since
       */
      private int getHeapsize() {
          return this.heapsize;
      }
  
      /**
       *Gets the cleanupthreadinterval attribute of the StoreJanitorImpl object
       *
       *@return    The cleanupthreadinterval value
       *@since
       */
      private int getCleanupthreadinterval() {
          return this.cleanupthreadinterval;
      }
  
      /**
       *Gets the priority attribute of the StoreJanitorImpl object
       *
       *@return    The priority value
       *@since
       */
      private int getPriority() {
          return this.priority;
      }
  
      /**
       *Gets the jVM attribute of the StoreJanitorImpl object
       *
       *@return    The jVM value
       *@since
       */
      private Runtime getJVM() {
          return this.jvm;
      }
  
      /**
       *Gets the storeList attribute of the StoreJanitorImpl object
       *
       *@return    The storeList value
       *@since
       */
      private ArrayList getStoreList() {
          return this.storelist;
      }
  
      /**
       * Starting at the next store, removes items, moving to the next again if necessary, until the specified number of items have been removed or all the stores are empty.
       *
       *@since
       */
      private void attemptToFreeStorage() {
          int storeListSize = getStoreList().size();
          int remove = getReduceBy();
          debug("number of stores is " + storeListSize + ", number of items to be removed is " + remove + ", if possible!");
          incIndex();
          for (int cnt = 0; cnt < storeListSize; incIndex(), cnt++) {// Look in all stores if necessary.
              if ((remove = reduceStoreBy(remove)) == 0) {
                  break;// Keep looking till all items removed,
              }
          }// or all stores are empty.
          if (remove < getReduceBy()) {// If items were removed call garbage collector.
              long gcTime = System.currentTimeMillis();
              jvm.gc();
              gcTime = System.currentTimeMillis() - gcTime;
              debug("items removed, so collecting garbage - took " + gcTime + "ms");
              debug("minFree=" + getMinFree() + ",      freeHeap=" + jvm.freeMemory() + ",          freeIsLow=" + freeIsLow());
          }
      }
  
      /**
       * Increment the store index.
       *
       *@since
       */
      private void incIndex() {
          if (++index >= getStoreList().size()) {
              index = 0;
          }
      }
  
      /**
       * Reduce the current store by the number of items specified, if possible.
       *
       *@param  remove  The number of items to be removed.
       *@return         the remaining count of items, that could not be removed.
       *@since
       */
      private int reduceStoreBy(int remove) {
          Store store = (Store) storelist.get(index);
          int sizeBefore = countSize(store);
          for (int i = 0; i < sizeBefore & remove > 0; i++, remove--) {
              store.free();
          }
          int sizeAfter = countSize(store);
          debug("store index=" + index + ", size before=" + sizeBefore + ",  size after=" + sizeAfter + ", removed=" + (sizeBefore - sizeAfter));
          return remove;
      }
  
      /**
       * To check if total memory is big enough to allow stores to be reduced.
       *
       *@return    true if big enough.
       *@since
       */
      private boolean heapIsBig() {
          return jvm.totalMemory() > getMaxHeap();
      }
  
      /**
       * To check if free memory is small enough to start reducing stores.
       *
       *@return    true if small enough.
       *@since
       */
      private boolean freeIsLow() {
          return jvm.freeMemory() < getMinFree();
      }
  
      /**
       * To calculate the minimum time in which the memory could be filled at the maximum rate of use.
       *
       *@return    the minimum time to fill.
       *@since
       */
      private long minTimeToFill() {
          return longDiv(jvm.freeMemory(), maxRateOfChange);
      }
  
      /**
       * Long division, guarding agains accidental divide by zero.
       *
       *@param  top     Description of Parameter
       *@param  bottom  Description of Parameter
       *@return         the result of division.
       *@since
       */
      private long longDiv(long top, long bottom) {
          try {
              return top / bottom;
          } catch (Exception e) {
              return top > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
          }
      }
  
      /**
       * Calculate the jvm memory in use now.
       *
       *@return    memory in use.
       *@since
       */
      private long inUseNow() {
          return jvm.totalMemory() - jvm.freeMemory();
      }
  
      /**
       * Count the size of a store.
       *
       *@param  store  Description of Parameter
       *@return        the size of the store.
       *@since
       */
      private int countSize(Store store) {
          int size = 0;
          Enumeration enum = store.keys();
          while (enum.hasMoreElements()) {
              size++;
              enum.nextElement();
          }
          return size;
      }
  
      /**
       * Shorten the call to print a debug message.
       *
       *@param  message  Description of Parameter
       *@since
       */
      private void debug(String message) {
          if (this.getLogger().isDebugEnabled()) {
              this.getLogger().debug(message);
          }
      }
  
  }
  
  
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org